diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..f7c68a0 --- /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 + +[*.json] +indent_style = space +indent_size = 2 +insert_final_newline = true + +[*.cmd] +indent_style = space +indent_size = 4 + +[*.{xml,config,slnx,csproj,props,targets}] +indent_style = space +indent_size = 2 + +[*.{txt,md}] +indent_style = space +indent_size = unset + +[**/Resources/**] +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 index 8079ae2..9607807 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,11 +3,8 @@ # Custom for Visual Studio *.cs diff=csharp -*.sln merge=union +*.slnx merge=union *.csproj merge=union -*.vbproj merge=union -*.fsproj merge=union -*.dbproj merge=union # Standard to msysgit *.doc diff=astextplain diff --git a/.gitignore b/.gitignore index f038c45..00376ec 100644 --- a/.gitignore +++ b/.gitignore @@ -1,28 +1,86 @@ +# Build Folders (you can keep bin if you'd like, to store dlls and pdbs) +[Bb]in/ +[Oo]bj/ + +# Node.js tools +node_modules/ +package-lock.json + +# NUnit test results +TestResult.xml + +# 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 -*.scc -*.sig -_ReSharper*/ +*.dbmdl +*.docstates *.orig -*.nupkg \ No newline at end of file +*.scc +*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/ +**/Resources/*.min.js \ No newline at end of file diff --git a/.nuget/NuGet.Config b/.nuget/NuGet.Config deleted file mode 100644 index 67f8ea0..0000000 --- a/.nuget/NuGet.Config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.nuget/NuGet.exe b/.nuget/NuGet.exe deleted file mode 100644 index 3ffdd33..0000000 Binary files a/.nuget/NuGet.exe and /dev/null differ diff --git a/.nuget/NuGet.targets b/.nuget/NuGet.targets deleted file mode 100644 index f943812..0000000 --- a/.nuget/NuGet.targets +++ /dev/null @@ -1,144 +0,0 @@ - - - - $(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; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Binaries/Microsoft Ajax Minifier/AjaxMinifier.exe b/Binaries/Microsoft Ajax Minifier/AjaxMinifier.exe deleted file mode 100644 index fed3b78..0000000 Binary files a/Binaries/Microsoft Ajax Minifier/AjaxMinifier.exe and /dev/null differ diff --git a/CHANGELOG.md b/CHANGELOG.md index cf0328f..cabeb69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,106 +1,317 @@ Change log ========== -## March 4, 2016 - v1.7.0 +## v3.3.1 - February 23, 2026 + * Was made refactoring + +## v3.3.0 - February 17, 2026 + * Optimized a memory usage in the `ReflectionHelpers.GetBestFitMethod` method + * Added support for .NET Standard 2.1 and .NET 10 + * Performed a migration to the modern C# null/not-null checks + * In the `lock` statements for .NET 10 target now uses a instances of the `System.Threading.Lock` class + * Reduced a memory allocation by using collection expressions + * The value of a read-only field in an embedded object or type can no longer be changed + +## v3.2.5 - March 1, 2024 + * Added a `README.md` file to NuGet package + +## v3.2.4 - January 8, 2024 + * Fixed a error that occurred in the `ReflectionHelpers.IsAllowedProperty` method when running on .NET Core 1.0 + +## v3.2.3 - December 6, 2023 + * JSON2 library was updated to version of May 10, 2023 + +## v3.2.2 - April 10, 2023 + * Fixed a error due to which the global object to become read-only after embedding of the host objects and types. Special thanks to [Denis Pushkarev](https://github.com/zloirock). + +## v3.2.1 - February 27, 2023 + * In JsRT modes improved a performance of .NET methods projection + +## v3.2.0 - February 26, 2023 + * In JavaScript engine settings was added one new property - `AllowReflection` (default `false`) + +## v3.1.0 - February 6, 2023 + * In JsRT modes, `JsVariantToValue` and `JsValueToVariant` native methods are no longer used for embedding objects and types + * JSON2 library was updated to version of October 30, 2022 + +## v3.0.9 - July 20, 2022 + * Now a case-sensitive cultural postfixes are used in the names of `.resx` files + +## v3.0.8 - August 8, 2021 + * Improved a implementation of the `Dispose` method + +## v3.0.7 - March 8, 2020 + * Fixed a error “Program crash after function call with too much parameters” + +## v3.0.6 - December 16, 2019 + * Fixed a errors leading to null reference exceptions in the `ReflectionHelpers` class. Special thanks to [Vanjoge](https://github.com/vanjoge). + +## v3.0.5 - October 21, 2019 + * Fixed a error that caused a crash during finalization + * In JsRT modes during calling of the `CollectGarbage` method is again not performed blocking + +## v3.0.4 - October 9, 2019 + * Slightly improved performance + * In JsRT modes the `CollectGarbage` method is called synchronously again + * Enabled a SourceLink in NuGet package + +## v3.0.3 - April 28, 2019 + * Fixed a error that occurred during the recursive execution and evaluation of JS files (while without correct handling of host exception) + +## v3.0.2 - March 13, 2019 + * In version for .NET Core improved a embedding of delegates and types + +## v3.0.1 - January 23, 2019 + * Fixed a error, that occurred in the `Classic` mode during calling an embedded delegate, which does not return a result + * Fixed a error, that occurred during setting a value to field of embedded type + * Improved a performance of the embedding of objects and types + * Accelerated a conversion of script types to host types + +## v3.0.0 - December 24, 2018 + * Format of the error messages was unified + * Created a new exception classes: `JsCompilationException`, `JsEngineException`, `JsFatalException`, `JsInterruptedException`, `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 + * `Format` method of the `JsErrorHelpers` class was renamed to the `GenerateErrorDetails` + * One part of the auxiliary code was removed, and other part moved to an external library - [AdvancedStringBuilder](https://github.com/Taritsyn/AdvancedStringBuilder) + * Added a ability to interrupt execution of the script + * In JsRT modes added a ability to pre-compile scripts + * In `MsieJsEngine` class was added `SupportsScriptPrecompilation` property and four new methods: `Interrupt`, `Precompile`, `PrecompileFile` and `PrecompileResource` + * In JavaScript engine settings was added one new property - `MaxStackSize` (default `492` or `984` KB) + * Added support of .NET Standard 2.0 (only supported `ChakraIeJsRt` and `ChakraEdgeJsRt` modes) + +## v3.0.0 RC 2 - December 4, 2018 + * Improved performance of debugging in ActiveScript modes + * `GetSourceFragmentFromLine` method of `JsErrorHelpers` class has been replaced by the `GetTextFragmentFromLine` method of `TextHelpers` class + * One part of the auxiliary code was removed, and other part moved to an external library - [AdvancedStringBuilder](https://github.com/Taritsyn/AdvancedStringBuilder) + * In the `IeNativeMethods` and `EdgeNativeMethods` classes for the `netstandard` targets was changed a calling convention from `StdCall` to `Cdecl` + +## v2.2.10 - November 20, 2018 + * Improved performance of debugging in ActiveScript modes + * JSON2 library was updated to version of June 12, 2017 + +## v3.0.0 RC 1 - September 18, 2018 + * In JavaScript engine settings was added one new property - `MaxStackSize` (default `492` or `984` KB) + * JSON2 library was updated to version of June 12, 2017 + +## v3.0.0 Beta 5 - August 23, 2018 + * Fixed a error, that occurred during the generation of error message + +## v2.2.9 - June 12, 2018 + * Changed a implementation of the `Dispose` method + +## v3.0.0 Beta 4 - June 6, 2018 + * Changed a implementation of the `Dispose` method + +## v3.0.0 Beta 3 - May 29, 2018 + * Fixed a [error #18](https://github.com/Taritsyn/MsieJavaScriptEngine/issues/18) “Block finalizer solved?” + +## v2.2.8 - May 24, 2018 + * Fixed a [error #18](https://github.com/Taritsyn/MsieJavaScriptEngine/issues/18) “Block finalizer solved?” + +## v3.0.0 Beta 2 - May 22, 2018 + * In `MsieJsEngine` class was added `SupportsScriptPrecompilation` property and three new methods: `Precompile`, `PrecompileFile` and `PrecompileResource` + * In JsRT modes added a ability to pre-compile scripts + +## v2.2.7 - April 10, 2018 + * Fixed a minor errors + +## v3.0.0 Beta 1 - April 8, 2018 + * Format of the error messages was unified + * Created a new exception classes: `JsCompilationException`, `JsEngineException`, `JsFatalException` 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 + * `Format` method of the `JsErrorHelpers` class was renamed to the `GenerateErrorDetails` + +## v2.2.6 - February 23, 2018 + * In JsRT modes during calling of the `CollectGarbage` method is no longer performed blocking + +## v2.2.5 - December 23, 2017 + * Removed a redundant code + * Fixed a error, that occurred in the `Classic` mode during removing the embedded host objects and types + * 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 3 - December 10, 2017 + * Added support of .NET Standard 2.0 + * Fixed a error, that occurred in the `Classic` mode during removing the embedded host objects and types + * Removed a redundant code + * In JsRT modes during calling of the `CollectGarbage` method is no longer performed blocking + +## v3.0.0 Alpha 2 - November 17, 2017 + * 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 1 - September 13, 2017 + * Added a ability to interrupt execution of the script + +## v2.2.4 - August 25, 2017 + * In ActiveScript modes now are uses the short names of error categories + * In `Classic` mode during debugging now script error contains a full stack trace + * In JsRT modes the compilation error messages now contains a information about the error location + +## v2.2.3 - July 4, 2017 + * Now during the rethrowing of exceptions are preserved the full call stack trace + * Reduced a number of delegate-wrappers + +## v2.2.2 - June 28, 2017 + * Switched to Apache license + * In JsRT modes fixed a problems in calculation of error locations + * An attempt was made to prevent occurrence of the access violation exception + * Now the original exception is added to instance of the `JsRuntimeException` class as an inner exception + * An attempt was made to prevent a blocking of finalizer's thread + * Added support of identifier names compliant with ECMAScript 5 + +## v2.2.1 - April 25, 2017 + * Now during debugging in ActiveScript modes the script error contains a error location + +## v2.2.0 - April 21, 2017 + * Added support of .NET Core 1.0.4 + * In JsRT modes now script error contains a full stack trace + * In `MsieJsEngine` class was added overloaded versions of the `Evaluate`, `Evaluate` and `Execute` methods, which take the document name as second parameter + * Now all modes support the possibility to debug in Visual Studio by adding the `debugger` statement to script code + +## v2.1.2 - February 12, 2017 + * Fixed a error causing a crash during finalization + +## v2.1.1 - February 10, 2017 + * Was made refactoring + +## v2.1.0 - December 19, 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 + * Fixed a error “Out of stack space” + * JSON2 library was updated to version of October 28, 2016 + +## v2.0.2 - December 2, 2016 + * Another attempt to prevent occurrence of the access violation exception in the `CallFunction` method + +## v2.0.1 - November 8, 2016 + * All exceptions made serializable + +## v2.0.0 - September 19, 2016 + * Added support of .NET Core 1.0.1 (only supported `ChakraIeJsRt` and `ChakraEdgeJsRt` modes) and .NET Framework 4.5.1 + * Added the `CollectGarbage` method + +## v2.0.0 Beta 2 - September 17, 2016 + * Added support of .NET Core 1.0.1 + +## v2.0.0 Beta 1 - September 9, 2016 + * Added the `CollectGarbage` method + +## v2.0.0 Alpha 1 - September 3, 2016 + * Added support of .NET Core 1.0 (only supported `ChakraIeJsRt` and `ChakraEdgeJsRt` modes) and .NET Framework 4.5.1 + +## v1.7.2 - August 17, 2016 + * An attempt was made to prevent occurrence of the access violation exception in the `CallFunction` method + +## v1.7.1 - May 24, 2016 + * JSON2 library was updated to version of May 10, 2016 + +## v1.7.0 - March 4, 2016 * Added the `EmbedHostObject` method (embeds a instance of simple class, structure or delegate to script code) * Added the `EmbedHostType` method (embeds a host type to script code) * Added a possibility to debug in Visual Studio by adding the `debugger` statement to script code. This feature only works in the `ChakraIeJsRt` and `ChakraEdgeJsRt` modes. * In JavaScript engine settings was added one new property - `EnableDebugging` (default `false`) * Improved implementation of the `CallFunction` method for Chakra JsRT modes -## February 26, 2016 - v1.7.0 Beta 1 +## v1.7.0 Beta 1 - February 26, 2016 * Added the `EmbedHostType` method (embeds a host type to script code) -## January 16, 2016 - v1.7.0 Alpha 2 +## v1.7.0 Alpha 2 - January 16, 2016 * Added a possibility to debug in Visual Studio by adding the `debugger` statement to script code. This feature only works in the `ChakraIeJsRt` and `ChakraEdgeJsRt` modes. * In JavaScript engine settings was added one new property - `EnableDebugging` (default `false`) -## January 5, 2016 - v1.7.0 Alpha 1 +## v1.7.0 Alpha 1 - January 5, 2016 * Added the `EmbedHostObject` method (embeds a instance of simple class, structure or delegate to script code) * Improved implementation of the `CallFunction` method for Chakra JsRT modes -## December 3, 2015 - v1.6.0 +## v1.6.0 - December 3, 2015 * Added support of “Edge” JsRT version of Chakra JavaScript engine * `ChakraJsRt` mode was renamed to `ChakraIeJsRt` -## July 23, 2015 - v1.5.6 +## v1.5.6 - July 23, 2015 * Source code of the `ChakraJsRtJsEngine` was synchronized with the Chakra Sample Hosts version of July 11, 2015 -## June 29, 2015 - v1.5.5 +## v1.5.5 - June 29, 2015 * Fixed an error, that occurs on computers with IE 6 * Removed `Obsolete` attribute from parameterless constructor -## June 28, 2015 - v1.5.4 +## v1.5.4 - June 28, 2015 * In `ChakraActiveScript` mode added native support of ECMAScript 5 (without polyfills) * Added `JsEngineSettings` class for any reason in the future to abandon redundant constructors -## May 5, 2015 - v1.5.3 +## v1.5.3 - May 5, 2015 * JSON2 library was updated to version of May 3, 2015 -## April 5, 2015 - v1.5.2 +## v1.5.2 - April 5, 2015 * JSON2 library was updated to version of February 25, 2015 -## January 13, 2015 - v1.5.1 +## v1.5.1 - January 13, 2015 * In ECMAScript 5 Polyfill added polyfill for the `String.prototype.split` method -## October 12, 2014 - v1.5.0 +## v1.5.0 - October 12, 2014 * Removed dependency on `System.Web.Extensions` * Assembly is now targeted on the .NET Framework 4 Client Profile -## July 22, 2014 - v1.4.4 +## v1.4.4 - July 22, 2014 * Source code of the `ChakraJsRtJsEngine` was synchronized with the Chakra Sample Hosts version of July 22, 2014 -## April 27, 2014 - v1.4.3 +## v1.4.3 - April 27, 2014 * In solution was enabled NuGet package restore * Fixed [JavaScriptEngineSwitcher.Msie's bug #7](https://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/7) "MsieJavaScriptEngine.ActiveScript.ActiveScriptException not wrapped" -## March 24, 2014 - v1.4.2 - * Fixed [JavaScriptEngineSwitcher.Msie's bug #5](http://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/5) "MSIE "Catastrophic failure" when disposing" +## v1.4.2 - March 24, 2014 + * Fixed [JavaScriptEngineSwitcher.Msie's bug #5](https://github.com/Taritsyn/JavaScriptEngineSwitcher/issues/5) "MSIE "Catastrophic failure" when disposing" -## March 22, 2014 - v1.4.1 +## v1.4.1 - March 22, 2014 * Fixed minor bugs -## February 27, 2014 - v1.4.0 +## v1.4.0 - February 27, 2014 * Removed following methods: `HasProperty`, `GetPropertyValue`, `SetPropertyValue` and `RemoveProperty` - * Fixed [bug #3](http://github.com/Taritsyn/MsieJavaScriptEngine/issues/3) "execute code from different threads" + * Fixed [bug #3](https://github.com/Taritsyn/MsieJavaScriptEngine/issues/3) "execute code from different threads" * Now in the `ChakraJsRt` mode is available a more detailed information about errors * In ECMAScript 5 Polyfill improved a performance of the `String.prototype.trim` method * JSON2 library was updated to version of February 4, 2014 -## January 16, 2014 - v1.3.0 +## v1.3.0 - January 16, 2014 * Added support of the JsRT version of Chakra * Now the MSIE JavaScript Engine can work in 4 modes: `Auto` (selected by default), `Classic`, `ChakraActiveScript` and `ChakraJsRt` * Following methods are obsolete: `HasProperty`, `GetPropertyValue`, `SetPropertyValue` and `RemoveProperty` -## December 30, 2013 - v1.2.0 +## v1.2.0 - December 30, 2013 * Fixed errors in ECMAScript 5 Polyfill * Added support of JavaScript `undefined` type -## September 3, 2013 - v1.1.3 +## v1.1.3 - September 3, 2013 * Access modifier of the `JsEngineLoadException` class has changed to public -## June 20, 2013 - v1.1.2 +## v1.1.2 - June 20, 2013 * JSON2 library was updated to version of May 26, 2013 -## October 15, 2012 - v1.1.1 +## v1.1.1 - October 15, 2012 * Assembly `MsieJavaScriptEngine.dll` now signed -## October 11, 2012 - v1.1.0 - * Added ability of using the Douglas Crockford's [JSON2](http://github.com/douglascrockford/JSON-js) library +## v1.1.0 - October 11, 2012 + * Added ability of using the Douglas Crockford's [JSON2](https://github.com/douglascrockford/JSON-js) library * By default using of the JSON2 library is disabled -## September 21, 2012 - v1.0.8 +## v1.0.8 - September 21, 2012 * Changed the format of error messages -## September 9, 2012 - v1.0.7 +## v1.0.7 - September 9, 2012 * Added the `ActiveScriptErrorFormatter` class -## August 29, 2012 - v1.0.5 - * [JavaScript Array Polyfills from TutorialsPoint.com](http://www.tutorialspoint.com/javascript/) was replaced by the Douglas Crockford's [ECMAScript 5 Polyfill](http://nuget.org/packages/ES5) +## v1.0.5 - August 29, 2012 + * [JavaScript Array Polyfills from TutorialsPoint.com](https://www.tutorialspoint.com/javascript/) was replaced by the Douglas Crockford's [ECMAScript 5 Polyfill](https://www.nuget.org/packages/ES5) * By default using of the ECMAScript 5 Polyfill is disabled -## August 27, 2012 - v1.0.1 +## v1.0.1 - August 27, 2012 * Added the `JsEngineLoadException` class -## August 26, 2012 - v1.0.0 +## v1.0.0 - August 26, 2012 * Initial version uploaded \ No newline at end of file diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 0000000..7743622 --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index 6cfac88..0000000 --- a/LICENSE.md +++ /dev/null @@ -1,49 +0,0 @@ -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 diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..97d57b4 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,203 @@ + Copyright (c) 2012-2026 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/cross-browser-split-license.txt b/Licenses/cross-browser-split-license.txt new file mode 100644 index 0000000..dfdee2c --- /dev/null +++ b/Licenses/cross-browser-split-license.txt @@ -0,0 +1,4 @@ +Cross-Browser Split 1.1.1 +Copyright 2007-2012 Steven Levithan +Available under the MIT License +ECMAScript compliant, uniform cross-browser split method \ No newline at end of file diff --git a/Licenses/jsrt-dotnet-license.txt b/Licenses/jsrt-dotnet-license.txt new file mode 100644 index 0000000..254be98 --- /dev/null +++ b/Licenses/jsrt-dotnet-license.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015 Rob Paveza + +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/microsoft-ajax-minifier-license.txt b/Licenses/microsoft-ajax-minifier-license.txt deleted file mode 100644 index a26edf0..0000000 --- a/Licenses/microsoft-ajax-minifier-license.txt +++ /dev/null @@ -1,140 +0,0 @@ -Apache License 2.0 (Apache) - -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: - -1. You must give any other recipients of the Work or Derivative Works a copy of this License; and - -2. You must cause any modified files to carry prominent notices stating that You changed the files; and - -3. 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 - -4. 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. \ No newline at end of file diff --git a/Licenses/uglify-js-license.txt b/Licenses/uglify-js-license.txt new file mode 100644 index 0000000..d206cc7 --- /dev/null +++ b/Licenses/uglify-js-license.txt @@ -0,0 +1,29 @@ +UglifyJS is released under the BSD license: + +Copyright 2012-2024 (c) Mihai Bazon + +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 HOLDER “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 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/MsieJavaScriptEngine.sln b/MsieJavaScriptEngine.sln deleted file mode 100644 index fa8361b..0000000 --- a/MsieJavaScriptEngine.sln +++ /dev/null @@ -1,83 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{7179145C-46AE-47D9-AD3B-DC4A3035A4DF}" - 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", "{1ACC225D-6787-47AB-AF34-C6021002390C}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MsieJavaScriptEngine", "src\MsieJavaScriptEngine\MsieJavaScriptEngine.csproj", "{D672BC49-C454-4975-BD25-A555B9BDD793}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{2A0DC227-73C5-4E3A-853A-83007AD56B85}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SharedFiles", "SharedFiles", "{76F4C14E-54ED-42AC-84F7-2072B14ED59E}" - ProjectSection(SolutionItems) = preProject - test\SharedFiles\link.txt = test\SharedFiles\link.txt - test\SharedFiles\square.js = test\SharedFiles\square.js - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MsieJavaScriptEngine.Test.Common", "test\MsieJavaScriptEngine.Test.Common\MsieJavaScriptEngine.Test.Common.csproj", "{A4085B9E-A5D3-4749-BD07-6D1EB7C23820}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MsieJavaScriptEngine.Test.Auto", "test\MsieJavaScriptEngine.Test.Auto\MsieJavaScriptEngine.Test.Auto.csproj", "{A4874C43-2097-4FDB-882A-0D74E5508BBD}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MsieJavaScriptEngine.Test.ChakraEdgeJsRt", "test\MsieJavaScriptEngine.Test.ChakraEdgeJsRt\MsieJavaScriptEngine.Test.ChakraEdgeJsRt.csproj", "{16DBAC89-4FFB-40AC-B437-FE5C8D6B6731}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MsieJavaScriptEngine.Test.ChakraIeJsRt", "test\MsieJavaScriptEngine.Test.ChakraIeJsRt\MsieJavaScriptEngine.Test.ChakraIeJsRt.csproj", "{7064E0DB-0B73-4534-84D0-1C96DA7E5AD1}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MsieJavaScriptEngine.Test.ChakraActiveScript", "test\MsieJavaScriptEngine.Test.ChakraActiveScript\MsieJavaScriptEngine.Test.ChakraActiveScript.csproj", "{21EA96CC-71E2-4CDD-B95A-C75EBD4AF2FA}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MsieJavaScriptEngine.Test.Classic", "test\MsieJavaScriptEngine.Test.Classic\MsieJavaScriptEngine.Test.Classic.csproj", "{50453B82-ACBF-4E25-9582-1113F274D53E}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D672BC49-C454-4975-BD25-A555B9BDD793}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D672BC49-C454-4975-BD25-A555B9BDD793}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D672BC49-C454-4975-BD25-A555B9BDD793}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D672BC49-C454-4975-BD25-A555B9BDD793}.Release|Any CPU.Build.0 = Release|Any CPU - {A4085B9E-A5D3-4749-BD07-6D1EB7C23820}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A4085B9E-A5D3-4749-BD07-6D1EB7C23820}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A4085B9E-A5D3-4749-BD07-6D1EB7C23820}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A4085B9E-A5D3-4749-BD07-6D1EB7C23820}.Release|Any CPU.Build.0 = Release|Any CPU - {A4874C43-2097-4FDB-882A-0D74E5508BBD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {A4874C43-2097-4FDB-882A-0D74E5508BBD}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A4874C43-2097-4FDB-882A-0D74E5508BBD}.Release|Any CPU.ActiveCfg = Release|Any CPU - {A4874C43-2097-4FDB-882A-0D74E5508BBD}.Release|Any CPU.Build.0 = Release|Any CPU - {16DBAC89-4FFB-40AC-B437-FE5C8D6B6731}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {16DBAC89-4FFB-40AC-B437-FE5C8D6B6731}.Debug|Any CPU.Build.0 = Debug|Any CPU - {16DBAC89-4FFB-40AC-B437-FE5C8D6B6731}.Release|Any CPU.ActiveCfg = Release|Any CPU - {16DBAC89-4FFB-40AC-B437-FE5C8D6B6731}.Release|Any CPU.Build.0 = Release|Any CPU - {7064E0DB-0B73-4534-84D0-1C96DA7E5AD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7064E0DB-0B73-4534-84D0-1C96DA7E5AD1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7064E0DB-0B73-4534-84D0-1C96DA7E5AD1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7064E0DB-0B73-4534-84D0-1C96DA7E5AD1}.Release|Any CPU.Build.0 = Release|Any CPU - {21EA96CC-71E2-4CDD-B95A-C75EBD4AF2FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {21EA96CC-71E2-4CDD-B95A-C75EBD4AF2FA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {21EA96CC-71E2-4CDD-B95A-C75EBD4AF2FA}.Release|Any CPU.ActiveCfg = Release|Any CPU - {21EA96CC-71E2-4CDD-B95A-C75EBD4AF2FA}.Release|Any CPU.Build.0 = Release|Any CPU - {50453B82-ACBF-4E25-9582-1113F274D53E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {50453B82-ACBF-4E25-9582-1113F274D53E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {50453B82-ACBF-4E25-9582-1113F274D53E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {50453B82-ACBF-4E25-9582-1113F274D53E}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {D672BC49-C454-4975-BD25-A555B9BDD793} = {1ACC225D-6787-47AB-AF34-C6021002390C} - {A4085B9E-A5D3-4749-BD07-6D1EB7C23820} = {2A0DC227-73C5-4E3A-853A-83007AD56B85} - {A4874C43-2097-4FDB-882A-0D74E5508BBD} = {2A0DC227-73C5-4E3A-853A-83007AD56B85} - {16DBAC89-4FFB-40AC-B437-FE5C8D6B6731} = {2A0DC227-73C5-4E3A-853A-83007AD56B85} - {7064E0DB-0B73-4534-84D0-1C96DA7E5AD1} = {2A0DC227-73C5-4E3A-853A-83007AD56B85} - {21EA96CC-71E2-4CDD-B95A-C75EBD4AF2FA} = {2A0DC227-73C5-4E3A-853A-83007AD56B85} - {50453B82-ACBF-4E25-9582-1113F274D53E} = {2A0DC227-73C5-4E3A-853A-83007AD56B85} - {76F4C14E-54ED-42AC-84F7-2072B14ED59E} = {2A0DC227-73C5-4E3A-853A-83007AD56B85} - EndGlobalSection -EndGlobal diff --git a/MsieJavaScriptEngine.slnx b/MsieJavaScriptEngine.slnx new file mode 100644 index 0000000..4508614 --- /dev/null +++ b/MsieJavaScriptEngine.slnx @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/NuGet/MsieJavaScriptEngine.nuspec b/NuGet/MsieJavaScriptEngine.nuspec deleted file mode 100644 index 06e1dbf..0000000 --- a/NuGet/MsieJavaScriptEngine.nuspec +++ /dev/null @@ -1,35 +0,0 @@ - - - - MsieJavaScriptEngine - 1.7.0 - MSIE JavaScript Engine for .NET - Andrey Taritsyn - Andrey Taritsyn - http://github.com/Taritsyn/MsieJavaScriptEngine/blob/master/LICENSE.md - http://github.com/Taritsyn/MsieJavaScriptEngine - http://i.imgur.com/cbiHK.png - false - This library is a .NET wrapper for working with the JavaScript engines of Internet Explorer and Edge (JsRT versions of Chakra, ActiveScript version of Chakra and Classic JavaScript Engine). Project was based on the code of SassAndCoffee.JavaScript (http://github.com/paulcbetts/SassAndCoffee) and Chakra Sample Hosts (http://github.com/panopticoncentral/chakra-host). - This library is a .NET wrapper for working with the JavaScript engines of Internet Explorer and Edge (JsRT versions of Chakra, ActiveScript version of Chakra and Classic JavaScript Engine). - 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); -3. Added a possibility to debug in Visual Studio by adding the `debugger` statement to script code. This feature only works in the `ChakraIeJsRt` and `ChakraEdgeJsRt` modes. -4. In JavaScript engine settings was added one new property - `EnableDebugging` (default `false`); -5. Improved implementation of the `CallFunction` method for Chakra JsRT modes. - Copyright (c) 2012-2016 Andrey Taritsyn - http://www.taritsyn.ru - en-US - JavaScript ECMAScript MSIE IE Edge Chakra - - - - - - - - - - - - - \ No newline at end of file diff --git a/NuGet/build-package.cmd b/NuGet/build-package.cmd deleted file mode 100644 index 9b71b05..0000000 --- a/NuGet/build-package.cmd +++ /dev/null @@ -1,2 +0,0 @@ -\Windows\Microsoft.NET\Framework\v4.0.30319\msbuild.exe ..\src\MsieJavaScriptEngine\MsieJavaScriptEngine.csproj /p:Configuration=Release -..\.nuget\nuget.exe pack MsieJavaScriptEngine.nuspec \ No newline at end of file diff --git a/NuGet/readme.txt b/NuGet/readme.txt deleted file mode 100644 index e97fab6..0000000 --- a/NuGet/readme.txt +++ /dev/null @@ -1,39 +0,0 @@ - - - ---------------------------------------------------------------------- - README file for MSIE JavaScript Engine for .NET v1.7.0 - - ---------------------------------------------------------------------- - - Copyright (c) 2012-2016 Andrey Taritsyn - http://www.taritsyn.ru - - - =========== - DESCRIPTION - =========== - This library is a .NET wrapper for working with the JavaScript engines - of Internet Explorer and Edge (JsRT versions of Chakra, ActiveScript - version of Chakra and Classic JavaScript Engine). - Project was based on the code of SassAndCoffee.JavaScript - (http://github.com/paulcbetts/SassAndCoffee) and Chakra Sample Hosts - (http://github.com/panopticoncentral/chakra-host). - - ============= - 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); - 3. Added a possibility to debug in Visual Studio by adding the - `debugger` statement to script code. This feature only works in the - `ChakraIeJsRt` and `ChakraEdgeJsRt` modes. - 4. In JavaScript engine settings was added one new property - - `EnableDebugging` (default `false`); - 5. Improved implementation of the `CallFunction` method for Chakra - JsRT modes. - - ============ - PROJECT SITE - ============ - http://github.com/Taritsyn/MsieJavaScriptEngine \ No newline at end of file diff --git a/README.md b/README.md index c22a339..ddbfcdc 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,18 @@ -MSIE JavaScript Engine for .NET +MSIE JavaScript Engine for .NET [![NuGet version](http://img.shields.io/nuget/v/MsieJavaScriptEngine.svg)](https://www.nuget.org/packages/MsieJavaScriptEngine/) [![Download count](https://img.shields.io/nuget/dt/MsieJavaScriptEngine.svg)](https://www.nuget.org/packages/MsieJavaScriptEngine/) =============================== ![MSIE JS Engine Logo](http://i.imgur.com/T3K5q.png) -This project is a .NET wrapper for working with the JavaScript engines of Internet Explorer and Edge (JsRT versions of Chakra, ActiveScript version of Chakra and Classic JavaScript Engine). -Project was based on the code of [SassAndCoffee.JavaScript](http://github.com/paulcbetts/SassAndCoffee) and [Chakra Sample Hosts](http://github.com/panopticoncentral/chakra-host). +This project is a .NET wrapper for working with the JavaScript engines of Internet Explorer and Edge Legacy (JsRT versions of Chakra, ActiveScript version of Chakra and Classic JavaScript Engine). +Project was based on the code of [SassAndCoffee.JavaScript](https://github.com/anaisbetts/SassAndCoffee), [Chakra Sample Hosts](https://github.com/panopticoncentral/chakra-host) and [jsrt-dotnet](https://github.com/robpaveza/jsrt-dotnet). -MSIE JavaScript Engine requires a installation of Internet Explorer or Edge on the machine and can work in 5 modes, that are defined in the JsEngineMode enumeration: +MSIE JavaScript Engine requires a installation of Internet Explorer or Edge Legacy on the machine and can work in 5 modes, that are defined in the JsEngineMode enumeration: * `Auto`. Automatically selects the most modern JavaScript engine from available on the machine. - * `Classic`. 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. - * `ChakraActiveScript`. ActiveScript version of Chakra JavaScript engine (supports ECMAScript 5). Requires Internet Explorer 9 or higher on the machine. - * `ChakraIeJsRt`. “IE” JsRT version of Chakra JavaScript engine (supports ECMAScript 5). Requires Internet Explorer 11 or Microsoft Edge on the machine. - * `ChakraEdgeJsRt`. “Edge” JsRT version of Chakra JavaScript engine (supports ECMAScript 5). Requires Microsoft Edge on the machine. + * `Classic`. 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. **Not supported in version for .NET Core.** + * `ChakraActiveScript`. ActiveScript version of Chakra JavaScript engine (supports ECMAScript 5). Requires Internet Explorer 9 or higher on the machine. **Not supported in version for .NET Core.** + * `ChakraIeJsRt`. “IE” JsRT version of Chakra JavaScript engine (supports ECMAScript 5). Requires Internet Explorer 11 or Microsoft Edge Legacy on the machine. + * `ChakraEdgeJsRt`. “Edge” JsRT version of Chakra JavaScript engine (supports ECMAScript 5). Requires Microsoft Edge Legacy on the machine. The supported .NET types are as follows: @@ -23,94 +23,129 @@ The supported .NET types are as follows: * `System.String` ## Installation -This library can be installed through NuGet - [http://nuget.org/packages/MsieJavaScriptEngine](http://nuget.org/packages/MsieJavaScriptEngine). +This library can be installed through NuGet - [https://www.nuget.org/packages/MsieJavaScriptEngine](https://www.nuget.org/packages/MsieJavaScriptEngine). ## Usage Consider a simple example of usage of the MSIE JavaScript Engine: ```csharp +using System; + +using MsieJavaScriptEngine; +using MsieJavaScriptEngine.Helpers; + namespace MsieJavaScriptEngine.Example.Console { - using System; - - using MsieJavaScriptEngine; - using MsieJavaScriptEngine.Helpers; - - class Program - { - static void Main(string[] args) - { - try - { - using (var jsEngine = new MsieJsEngine()) - { - const string expression = "7 * 8 - 20"; - var result = jsEngine.Evaluate(expression); - - Console.WriteLine("{0} = {1}", expression, result); - } - } - catch (JsEngineLoadException e) - { - Console.WriteLine("During loading of JavaScript engine an error occurred."); - Console.WriteLine(); - Console.WriteLine(JsErrorHelpers.Format(e)); - } - catch (JsRuntimeException e) - { - Console.WriteLine("During execution of JavaScript code an error occurred."); - Console.WriteLine(); - Console.WriteLine(JsErrorHelpers.Format(e)); - } - - Console.ReadLine(); - } - } + class Program + { + static void Main(string[] args) + { + try + { + using (var jsEngine = new MsieJsEngine()) + { + const string expression = "7 * 8 - 20"; + var result = jsEngine.Evaluate(expression); + + Console.WriteLine("{0} = {1}", expression, result); + } + } + catch (JsEngineLoadException e) + { + Console.WriteLine("During loading of JavaScript engine an error occurred."); + Console.WriteLine(); + Console.WriteLine(JsErrorHelpers.GenerateErrorDetails(e)); + } + catch (JsScriptException e) + { + Console.WriteLine("During processing of JavaScript code an error occurred."); + Console.WriteLine(); + Console.WriteLine(JsErrorHelpers.GenerateErrorDetails(e)); + } + catch (JsException e) + { + Console.WriteLine("During working of JavaScript engine an unknown error occurred."); + Console.WriteLine(); + Console.WriteLine(JsErrorHelpers.GenerateErrorDetails(e)); + } + + Console.ReadLine(); + } + } } ``` First we create an instance of the MsieJsEngine class. Then we evaluate a JavaScript expression by using of the `Evaluate` method and output its result to the console. -In addition, we provide handling of the following exception types: JsEngineLoadException and JsRuntimeException. +In addition, we provide handling of the following exception types: JsEngineLoadException, JsScriptException and JsException. +In the MSIE JavaScript Engine, exceptions have the following hierarchy: + + * JsException + * JsEngineException + * JsEngineLoadException + * JsFatalException + * JsScriptException + * JsCompilationException + * JsRuntimeException + * JsInterruptedException + * JsUsageException Also, when you create an instance of the MsieJsEngine class, then you can pass the JavaScript engine settings via the constructor. Consider in detail properties of the JsEngineSettings class: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Property nameData typeDefault valueDescription
EnableDebuggingBooleanfalseFlag for whether to allow debugging in Visual Studio by adding the `debugger` statement to script code (only works in the `ChakraIeJsRt` and `ChakraEdgeJsRt` modes).
EngineModeJsEngineMode enumerationAutoJavaScript engine mode.
UseEcmaScript5PolyfillBooleanfalseFlag for whether to use the ECMAScript 5 Polyfill.
UseJson2LibraryBooleanfalseFlag for whether to use the JSON2 library
Property nameData typeDefault valueDescription
AllowReflectionBooleanfalse +

Flag for whether to allow the usage of reflection API in the script code.

+

This affects Object.GetType, Exception.GetType, Exception.TargetSite and Delegate.Method.

+
EnableDebuggingBooleanfalseFlag for whether to allow debugging in Visual Studio by adding the debugger statement to script code.
EngineModeJsEngineMode enumerationAutoJavaScript engine mode.
MaxStackSizeInt32503 808 or 1 007 616 +

Maximum stack size in bytes.

+

Set a 0 to use the default maximum stack size specified in the header for the executable.

+
UseEcmaScript5PolyfillBooleanfalseFlag for whether to use the ECMAScript 5 Polyfill.
UseJson2LibraryBooleanfalseFlag for whether to use the JSON2 library
@@ -118,20 +153,23 @@ Consider in detail properties of the + .NETFramework + v4.0 + client + + \ 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 0000000..543e8a3 --- /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 0000000..512142d --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "10.0.100", + "rollForward": "latestFeature" + } +} diff --git a/images/MsieJavaScriptEngine_Logo_128x128.png b/images/MsieJavaScriptEngine_Logo_128x128.png new file mode 100644 index 0000000..d53d471 Binary files /dev/null and b/images/MsieJavaScriptEngine_Logo_128x128.png differ diff --git a/run-tests.cmd b/run-tests.cmd new file mode 100644 index 0000000..d9818c7 --- /dev/null +++ b/run-tests.cmd @@ -0,0 +1,39 @@ +@echo off +setlocal + +set common_test_project_name=MsieJavaScriptEngine.Test.Common +set auto_test_project_name=MsieJavaScriptEngine.Test.Auto +set chakra_edge_jsrt_test_project_name=MsieJavaScriptEngine.Test.ChakraEdgeJsRt +set chakra_ie_jsrt_test_project_name=MsieJavaScriptEngine.Test.ChakraIeJsRt +set chakra_activescript_test_project_name=MsieJavaScriptEngine.Test.ChakraActiveScript +set classic_test_project_name=MsieJavaScriptEngine.Test.Classic + +set common-args=%* +set test_dir_path=test +set common_test_project_file_path=%test_dir_path%\%common_test_project_name%\%common_test_project_name%.csproj +set auto_test_project_file_path=%test_dir_path%\%auto_test_project_name%\%auto_test_project_name%.csproj +set chakra_edge_jsrt_test_project_file_path=%test_dir_path%\%chakra_edge_jsrt_test_project_name%\%chakra_edge_jsrt_test_project_name%.csproj +set chakra_ie_jsrt_test_project_file_path=%test_dir_path%\%chakra_ie_jsrt_test_project_name%\%chakra_ie_jsrt_test_project_name%.csproj +set chakra_activescript_test_project_file_path=%test_dir_path%\%chakra_activescript_test_project_name%\%chakra_activescript_test_project_name%.csproj +set classic_test_project_file_path=%test_dir_path%\%classic_test_project_name%\%classic_test_project_name%.csproj + +@echo Run unit tests... +@echo. + +dotnet test %common-args% "%common_test_project_file_path%" +@echo. + +dotnet test %common-args% "%auto_test_project_file_path%" +@echo. + +dotnet test %common-args% "%chakra_edge_jsrt_test_project_file_path%" +@echo. + +dotnet test %common-args% "%chakra_ie_jsrt_test_project_file_path%" +@echo. + +dotnet test %common-args% "%chakra_activescript_test_project_file_path%" +@echo. + +dotnet test %common-args% "%classic_test_project_file_path%" +@echo. \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/.uglifyjsrc b/src/MsieJavaScriptEngine/.uglifyjsrc new file mode 100644 index 0000000..8a91584 --- /dev/null +++ b/src/MsieJavaScriptEngine/.uglifyjsrc @@ -0,0 +1,11 @@ +{ + "compress": { + "hoist_funs": true, + "hoist_vars": true, + "passes": 2 + }, + "mangle": {}, + "output": { + "comments": "/^!/" + } +} diff --git a/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptException.cs b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptException.cs index 315e2fc..a005e9a 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptException.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptException.cs @@ -1,46 +1,101 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - using System; - using System.Runtime.Serialization; - using System.Runtime.InteropServices.ComTypes; +#if NETFRAMEWORK +using System; +using System.Runtime.Serialization; +using System.Security.Permissions; +namespace MsieJavaScriptEngine.ActiveScript +{ [Serializable] - internal sealed class ActiveScriptException : Exception + public sealed class ActiveScriptException : Exception { /// - /// Gets or sets a error code + /// The HRESULT of the error + /// + private int _errorCode; + + /// + /// Type of the script error + /// + private string _type = string.Empty; + + /// + /// Category of error + /// + private string _category = string.Empty; + + /// + /// Description of error + /// + private string _description = string.Empty; + + /// + /// Document name + /// + private string _documentName = string.Empty; + + /// + /// Line number on which the error occurred + /// + private uint _lineNumber; + + /// + /// Column number on which the error occurred + /// + private int _columnNumber; + + /// + /// String representation of the script call stack + /// + private string _callStack = string.Empty; + + /// + /// Source fragment + /// + private string _sourceFragment = string.Empty; + + /// + /// Gets or sets a HRESULT of the error /// public int ErrorCode { - get; - set; + get { return _errorCode; } + set { _errorCode = value; } + } + + /// + /// Gets or sets a type of the script error + /// + public string Type + { + get { return _type; } + set { _type = value; } } /// - /// Gets or sets a WCode + /// Gets or sets a category of error /// - public short ErrorWCode + public string Category { - get; - set; + get { return _category; } + set { _category = value; } } /// - /// Gets or sets a application specific source context + /// Gets or sets a description of error /// - public uint SourceContext + public string Description { - get; - set; + get { return _description; } + set { _description = value; } } /// - /// Gets or sets a subcategory of error + /// Gets or sets a document name /// - public string Subcategory + public string DocumentName { - get; - set; + get { return _documentName; } + set { _documentName = value; } } /// @@ -48,8 +103,8 @@ public string Subcategory /// public uint LineNumber { - get; - set; + get { return _lineNumber; } + set { _lineNumber = value; } } /// @@ -57,28 +112,32 @@ public uint LineNumber /// public int ColumnNumber { - get; - set; + get { return _columnNumber; } + set { _columnNumber = value; } } /// - /// Gets or sets a content of the line on which the error occurred + /// Gets or sets a string representation of the script call stack /// - public string SourceError + public string CallStack { - get; - set; + get { return _callStack; } + set { _callStack = value; } } - /// - /// Initializes a new instance of the class + /// Gets or sets a source fragment /// - public ActiveScriptException() - { } + public string SourceFragment + { + get { return _sourceFragment; } + set { _sourceFragment = value; } + } + /// /// Initializes a new instance of the class + /// with a specified error message /// /// The message public ActiveScriptException(string message) @@ -87,14 +146,8 @@ public ActiveScriptException(string message) /// /// Initializes a new instance of the class - /// - /// The inner exception - public ActiveScriptException(Exception innerException) - : base(null, 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 message /// The inner exception @@ -103,92 +156,55 @@ public ActiveScriptException(string message, Exception innerException) { } /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class with serialized data /// - /// The - /// that holds the serialized object data about the exception being thrown - /// The - /// that contains contextual information about the source or destination - /// - /// The parameter is null - /// - /// - /// The class name is null or is zero (0) - /// + /// The object that holds the serialized data + /// The contextual information about the source or destination private ActiveScriptException(SerializationInfo info, StreamingContext context) : base(info, context) - { } - - - internal static ActiveScriptException Create(IActiveScriptError error) { - string message = string.Empty; - int errorCode = 0; - short errorWCode = 0; - uint sourceContext = 0; - string subcategory = string.Empty; - string helpLink = string.Empty; - uint lineNumber = 0; - int columnNumber = 0; - string sourceError = string.Empty; - - try - { - error.GetSourceLineText(out sourceError); - } - catch + if (info is not null) { - // Do nothing + _errorCode = info.GetInt32("ErrorCode"); + _type = info.GetString("Type"); + _category = info.GetString("Category"); + _description = info.GetString("Description"); + _documentName = info.GetString("DocumentName"); + _lineNumber = info.GetUInt32("LineNumber"); + _columnNumber = info.GetInt32("ColumnNumber"); + _callStack = info.GetString("CallStack"); + _sourceFragment = info.GetString("SourceFragment"); } + } - try - { - error.GetSourcePosition(out sourceContext, out lineNumber, out columnNumber); - ++lineNumber; - ++columnNumber; - } - catch - { - // Do nothing - } + #region Exception overrides - try - { - EXCEPINFO excepInfo; - error.GetExceptionInfo(out excepInfo); - - message = excepInfo.bstrDescription; - subcategory = excepInfo.bstrSource; - errorCode = excepInfo.scode; - errorWCode = excepInfo.wCode; - if (!string.IsNullOrWhiteSpace(excepInfo.bstrHelpFile) - && excepInfo.dwHelpContext != 0) - { - helpLink = string.Format("{0}: {1}", excepInfo.bstrHelpFile, excepInfo.dwHelpContext); - } - else if (!string.IsNullOrWhiteSpace(excepInfo.bstrHelpFile)) - { - helpLink = excepInfo.bstrHelpFile; - } - } - catch + /// + /// 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 is null) { - // Do nothing + throw new ArgumentNullException("info"); } - var activeScriptException = new ActiveScriptException(message) - { - ErrorCode = errorCode, - ErrorWCode = errorWCode, - SourceContext = sourceContext, - Subcategory = subcategory, - LineNumber = lineNumber, - ColumnNumber = columnNumber, - SourceError = sourceError, - HelpLink = helpLink, - }; - - return activeScriptException; + base.GetObjectData(info, context); + info.AddValue("ErrorCode", _errorCode); + info.AddValue("Type", _type); + info.AddValue("Category", _category); + info.AddValue("Description", _description); + info.AddValue("DocumentName", _documentName); + info.AddValue("LineNumber", _lineNumber); + info.AddValue("ColumnNumber", _columnNumber); + info.AddValue("CallStack", _callStack); + info.AddValue("SourceFragment", _sourceFragment); } + + #endregion } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptJsEngineBase.ScriptSiteBase.cs b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptJsEngineBase.ScriptSiteBase.cs new file mode 100644 index 0000000..7e6749f --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptJsEngineBase.ScriptSiteBase.cs @@ -0,0 +1,343 @@ +#if NETFRAMEWORK +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Runtime.InteropServices; + +using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; + +using MsieJavaScriptEngine.ActiveScript.Debugging; +using MsieJavaScriptEngine.Constants; +using MsieJavaScriptEngine.Helpers; +using MsieJavaScriptEngine.Resources; + +namespace MsieJavaScriptEngine.ActiveScript +{ + internal abstract partial class ActiveScriptJsEngineBase + { + /// + /// Active Script site + /// + protected abstract class ScriptSiteBase : IActiveScriptSite, + IActiveScriptSiteDebug32, IActiveScriptSiteDebug64, + IActiveScriptSiteDebugEx, ICustomQueryInterface + { + /// + /// Instance of the Active Script JS engine + /// + private readonly ActiveScriptJsEngineBase _jsEngine; + + /// + /// Gets or sets a last Active Script exception + /// + public ActiveScriptException LastException + { + get { return _jsEngine._lastException; } + set { _jsEngine._lastException = value; } + } + + /// + /// Gets a instance of Active Script wrapper + /// + public IActiveScriptWrapper ActiveScriptWrapper + { + get { return _jsEngine._activeScriptWrapper; } + } + + /// + /// Gets a flag that indicates if the script interruption is requested + /// + public virtual bool InterruptRequested + { + get { return _jsEngine._interruptRequested; } + } + + + /// + /// Constructs an instance of the Active Script site + /// + /// Instance of the Active Script JS engine + protected ScriptSiteBase(ActiveScriptJsEngineBase jsEngine) + { + _jsEngine = jsEngine; + } + + + /// + /// Processes a Active Script error + /// + /// Instance of + protected virtual void ProcessActiveScriptError(IActiveScriptError error) + { + var activeScriptException = CreateActiveScriptException(error); + LastException = activeScriptException; + } + + /// + /// Creates a instance of + /// + /// Instance of + /// Instance of + protected ActiveScriptException CreateActiveScriptException(IActiveScriptError error) + { + EXCEPINFO exceptionInfo; + error.GetExceptionInfo(out exceptionInfo); + + int hResult = exceptionInfo.scode; + string message = string.Empty; + string category = string.Empty; + string description = string.Empty; + string type = string.Empty; + string helpLink = string.Empty; + string documentName = string.Empty; + uint lineNumber = 0; + int columnNumber = 0; + string callStack = string.Empty; + string sourceFragment = string.Empty; + + if (hResult == ComErrorCode.E_ABORT) + { + category = JsErrorCategory.Interrupted; + description = CommonStrings.Runtime_ScriptInterrupted; + message = description; + } + else + { + int errorNumber = ComHelpers.HResult.GetFacility(hResult) == ComErrorCode.FACILITY_CONTROL ? + ComHelpers.HResult.GetCode(hResult) : 0; + category = _jsEngine.ShortenErrorCategoryName(exceptionInfo.bstrSource); + description = exceptionInfo.bstrDescription; + type = _jsEngine.GetErrorTypeByNumber(errorNumber); + + if (!string.IsNullOrWhiteSpace(exceptionInfo.bstrHelpFile)) + { + helpLink = exceptionInfo.dwHelpContext != 0 ? + string.Format("{0}: {1}", exceptionInfo.bstrHelpFile, exceptionInfo.dwHelpContext) + : + exceptionInfo.bstrHelpFile + ; + } + + uint sourceContext = 0; + error.GetSourcePosition(out sourceContext, out lineNumber, out columnNumber); + ++lineNumber; + ++columnNumber; + + documentName = GetDocumentName(sourceContext); + + if (ActiveScriptJsErrorHelpers.IsCompilationError(errorNumber)) + { + string sourceLine; + error.GetSourceLineText(out sourceLine); + + sourceFragment = TextHelpers.GetTextFragmentFromLine(sourceLine, columnNumber); + } + else + { + callStack = JsErrorHelpers.StringifyCallStackItems(GetCallStackItems()); + } + + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, documentName, + (int)lineNumber, columnNumber, sourceFragment, callStack); + } + + var activeScriptException = new ActiveScriptException(message) + { + ErrorCode = hResult, + Type = type, + Category = category, + Description = description, + DocumentName = documentName, + LineNumber = lineNumber, + ColumnNumber = columnNumber, + CallStack = callStack, + SourceFragment = sourceFragment, + HelpLink = helpLink + }; + + return activeScriptException; + } + + /// + /// Gets a document name + /// + /// Application specific source context + /// Document name + private string GetDocumentName(uint sourceContext) + { + string documentName = string.Empty; + var documentKey = new UIntPtr(sourceContext); + DebugDocument document; + + if (_jsEngine._debugDocuments.TryGetValue(documentKey, out document)) + { + document.GetName(DocumentNameType.Title, out documentName); + } + else if (!_jsEngine._documentNames.TryGetValue(documentKey, out documentName)) + { + documentName = string.Empty; + } + + return documentName; + } + + /// + /// Gets a array of instances + /// + /// An array of instances + protected virtual CallStackItem[] GetCallStackItems() + { + return []; + } + + #region IActiveScriptSite implementation + + public void GetLcid(out int lcid) + { + lcid = CultureInfo.CurrentCulture.LCID; + } + + public void GetItemInfo(string name, ScriptInfoFlags mask, ref IntPtr pUnkItem, ref IntPtr pTypeInfo) + { + object item = _jsEngine._hostItems[name]; + if (item is null) + { + throw new COMException( + string.Format(NetFrameworkStrings.Runtime_ItemNotFound, name), + ComErrorCode.E_ELEMENT_NOT_FOUND + ); + } + + if (mask.HasFlag(ScriptInfoFlags.IUnknown)) + { + pUnkItem = Marshal.GetIDispatchForObject(item); + } + + if (mask.HasFlag(ScriptInfoFlags.ITypeInfo)) + { + pTypeInfo = Marshal.GetITypeInfoForType(item.GetType()); + } + } + + public void GetDocVersionString(out string version) + { + throw new NotImplementedException(); + } + + public void OnScriptTerminate(object result, EXCEPINFO exceptionInfo) + { } + + public void OnStateChange(ScriptState state) + { } + + public void OnScriptError(IActiveScriptError error) + { + ProcessActiveScriptError(error); + } + + public void OnEnterScript() + { } + + public void OnLeaveScript() + { } + + #endregion + + #region IActiveScriptSiteDebug32 and IActiveScriptSiteDebug64 implementation + + public void GetRootApplicationNode(out IDebugApplicationNode node) + { + _jsEngine._debugApplicationWrapper.GetRootNode(out node); + } + + public void OnScriptErrorDebug(IActiveScriptErrorDebug errorDebug, out bool enterDebugger, + out bool callOnScriptErrorWhenContinuing) + { + var error = errorDebug as IActiveScriptError; + if (error is not null) + { + ProcessActiveScriptError(error); + } + + enterDebugger = true; + callOnScriptErrorWhenContinuing = true; + } + + #region IActiveScriptSiteDebug32 implementation + + public void GetApplication(out IDebugApplication32 application) + { + application = _jsEngine._debugApplicationWrapper.DebugApplication32; + } + + public void GetDocumentContextFromPosition(uint sourceContext, uint offset, uint length, + out IDebugDocumentContext documentContext) + { + documentContext = null; + DebugDocument document; + + if (_jsEngine._debugDocuments.TryGetValue(new UIntPtr(sourceContext), out document)) + { + document.GetContextOfPosition(offset, length, out documentContext); + } + } + + #endregion + + #region IActiveScriptSiteDebug64 implementation + + public void GetApplication(out IDebugApplication64 application) + { + application = _jsEngine._debugApplicationWrapper.DebugApplication64; + } + + public void GetDocumentContextFromPosition(ulong sourceContext, uint offset, uint length, + out IDebugDocumentContext documentContext) + { + documentContext = null; + DebugDocument document; + + if (_jsEngine._debugDocuments.TryGetValue(new UIntPtr(sourceContext), out document)) + { + document.GetContextOfPosition(offset, length, out documentContext); + } + } + + #endregion + + #endregion + + #region IActiveScriptSiteDebugEx implementation + + public void OnCanNotJitScriptErrorDebug(IActiveScriptErrorDebug errorDebug, + out bool callOnScriptErrorWhenContinuing) + { + bool enterDebugger; + + OnScriptErrorDebug(errorDebug, out enterDebugger, out callOnScriptErrorWhenContinuing); + } + + #endregion + + #region ICustomQueryInterface implementation + + public CustomQueryInterfaceResult GetInterface(ref Guid iid, out IntPtr pInterface) + { + pInterface = IntPtr.Zero; + + if (iid == typeof(IActiveScriptSiteDebug32).GUID + || iid == typeof(IActiveScriptSiteDebug64).GUID + || iid == typeof(IActiveScriptSiteDebugEx).GUID) + { + return _jsEngine._processDebugManagerWrapper is not null ? + CustomQueryInterfaceResult.NotHandled : CustomQueryInterfaceResult.Failed; + } + + return CustomQueryInterfaceResult.NotHandled; + } + + #endregion + } + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptJsEngineBase.cs b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptJsEngineBase.cs index c3006a8..fbb4e44 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptJsEngineBase.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptJsEngineBase.cs @@ -1,24 +1,23 @@ -namespace MsieJavaScriptEngine.ActiveScript +#if NETFRAMEWORK +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Expando; + +using MsieJavaScriptEngine.ActiveScript.Debugging; +using MsieJavaScriptEngine.Constants; +using MsieJavaScriptEngine.Helpers; +using MsieJavaScriptEngine.Resources; +using MsieJavaScriptEngine.Utilities; + +namespace MsieJavaScriptEngine.ActiveScript { - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Reflection; - using System.Runtime.InteropServices; - using System.Runtime.InteropServices.Expando; - using System.Windows.Threading; - - using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; - - using Constants; - using Helpers; - using Resources; - using Utilities; - /// - /// Base class of the ActiveScript JavaScript engine + /// Active Script version of JS engine /// - internal abstract class ActiveScriptJsEngineBase : IInnerJsEngine, IActiveScriptSite + internal abstract partial class ActiveScriptJsEngineBase : InnerJsEngineBase { /// /// Name of resource, which contains a ECMAScript 5 Polyfill @@ -31,134 +30,187 @@ internal abstract class ActiveScriptJsEngineBase : IInnerJsEngine, IActiveScript private const string JSON2_LIBRARY_RESOURCE_NAME = "MsieJavaScriptEngine.Resources.json2.min.js"; /// - /// Pointer to an instance of native JavaScript engine + /// Instance of Active Script wrapper /// - private IntPtr _pActiveScript; + protected IActiveScriptWrapper _activeScriptWrapper; /// - /// Instance of native JavaScript engine + /// Instance of script dispatch /// - private IActiveScript _activeScript; + private IExpando _dispatch; /// - /// Instance of ActiveScriptParseWrapper + /// List of host items /// - private IActiveScriptParseWrapper _activeScriptParse; + protected Dictionary _hostItems = new Dictionary(); /// - /// Instance of script dispatch + /// Last Active Script exception /// - private IExpando _dispatch; + private ActiveScriptException _lastException; /// - /// List of host items + /// Instance of process debug manager wrapper /// - private Dictionary _hostItems = new Dictionary(); + private ProcessDebugManagerWrapper _processDebugManagerWrapper; /// - /// Host-defined document version string + /// Instance of debug application wrapper /// - private readonly string _documentVersion; + private DebugApplicationWrapper _debugApplicationWrapper; /// - /// Last ActiveScript exception + /// The cookie of the debug application /// - private ActiveScriptException _lastException; + private uint _debugApplicationCookie; /// - /// JavaScript engine mode + /// Flag indicating whether debugging started /// - private readonly JsEngineMode _engineMode; + private bool _debuggingStarted; /// - /// Name of JavaScript engine mode + /// List of document names /// - private readonly string _engineModeName; + private Dictionary _documentNames = new Dictionary(); /// - /// for the thread currently executing + /// List of debug documents /// - private readonly Dispatcher _dispatcher = Dispatcher.CurrentDispatcher; + private Dictionary _debugDocuments = new Dictionary(); /// - /// Flag that object is destroyed + /// Next source context /// - private StatedFlag _disposedFlag = new StatedFlag(); + private uint _nextSourceContext = 1; + /// + /// Lowest supported version of Internet Explorer + /// + private readonly string _lowerIeVersion; /// - /// Constructs an instance of the ActiveScript JavaScript engine + /// Prefix of error category name /// - /// CLSID of JavaScript engine - /// JavaScript engine mode - /// Lowest supported version of Internet Explorer + private readonly string _errorCategoryNamePrefix; + + /// + /// Flag that indicates if the script interruption is requested + /// + protected bool _interruptRequested; + + /// + /// Instance of script dispatcher + /// + private static ScriptDispatcher _dispatcher; + + /// + /// Synchronizer of script dispatcher initialization + /// + private static readonly Lock _dispatcherSynchronizer = new Lock(); + + + /// + /// Constructs an instance of the Active Script engine + /// + /// JS engine settings + /// CLSID of JS engine /// Version of script language - /// Flag for whether to use the ECMAScript 5 Polyfill - /// Flag for whether to use the JSON2 library - protected ActiveScriptJsEngineBase(string clsid, JsEngineMode engineMode, string lowerIeVersion, - ScriptLanguageVersion languageVersion, bool useEcmaScript5Polyfill, bool useJson2Library) + /// Lowest supported version of Internet Explorer + /// Prefix of error category name + protected ActiveScriptJsEngineBase(JsEngineSettings settings, string clsid, + ScriptLanguageVersion languageVersion, string lowerIeVersion, string errorCategoryNamePrefix) + : base(settings) { - _engineMode = engineMode; - _engineModeName = JsEngineModeHelpers.GetModeName(engineMode); - _pActiveScript = IntPtr.Zero; + InitScriptDispatcher(_settings.MaxStackSize); + + _lowerIeVersion = lowerIeVersion; + _errorCategoryNamePrefix = errorCategoryNamePrefix; try { - _pActiveScript = ComHelpers.CreateInstanceByClsid(clsid); - _activeScript = (IActiveScript)Marshal.GetObjectForIUnknown(_pActiveScript); + _dispatcher.Invoke(() => + { + _activeScriptWrapper = CreateActiveScriptWrapper(clsid, languageVersion); + + if (_settings.EnableDebugging) + { + StartDebugging(); + } + + _activeScriptWrapper.SetScriptSite(CreateScriptSite()); + _activeScriptWrapper.InitNew(); + _activeScriptWrapper.SetScriptState(ScriptState.Started); + LoadPolyfills(); + + _dispatch = WrapScriptDispatch(_activeScriptWrapper.GetScriptDispatch()); + }); } - catch (Exception e) + catch (COMException e) { - throw new JsEngineLoadException( - string.Format(Strings.Runtime_IeJsEngineNotLoaded, - _engineModeName, lowerIeVersion, e.Message), _engineModeName); + throw WrapCOMException(e); } + catch (ActiveScriptException e) + { + string description = e.Description; + string message = JsErrorHelpers.GenerateEngineLoadErrorMessage(description, _engineModeName, true); - if (languageVersion != ScriptLanguageVersion.None) + var wrapperEngineLoadException = new JsEngineLoadException(message, _engineModeName, e) + { + Description = description + }; + + throw wrapperEngineLoadException; + } + catch (InvalidOperationException e) + { + throw JsErrorHelpers.WrapEngineLoadException(e, _engineModeName); + } + catch (Exception e) + { + throw JsErrorHelpers.WrapEngineLoadException(e, _engineModeName, true); + } + finally { - var activeScriptProperty = _activeScript as IActiveScriptProperty; - if (activeScriptProperty != null) + if (_dispatch is null) { - object scriptLanguageVersion = (int)languageVersion; - uint result = activeScriptProperty.SetProperty((uint)ScriptProperty.InvokeVersioning, - IntPtr.Zero, ref scriptLanguageVersion); - if (result != (uint)ScriptHResult.Ok) - { - throw new JsEngineLoadException( - string.Format(Strings.Runtime_ActiveScriptLanguageVersionSelectionFailed, languageVersion)); - } + Dispose(); } } - - _activeScriptParse = new ActiveScriptParseWrapper(_pActiveScript, _activeScript); - _activeScriptParse.InitNew(); - - _activeScript.SetScriptSite(this); - _activeScript.SetScriptState(ScriptState.Started); - - InitScriptDispatch(); - _documentVersion = DateTime.UtcNow.ToString("o"); - - LoadResources(useEcmaScript5Polyfill, useJson2Library); } + /// - /// Destructs instance of ActiveScript JavaScript engine + /// Initializes a script dispatcher /// - ~ActiveScriptJsEngineBase() + /// 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. + private static void InitScriptDispatcher(int maxStackSize) { - Dispose(false); - } + if (_dispatcher is not null) + { + return; + } + lock (_dispatcherSynchronizer) + { + if (_dispatcher is not null) + { + return; + } + + _dispatcher = new ScriptDispatcher(maxStackSize); + } + } /// - /// Checks a support of the JavaScript engine on the machine + /// Checks a support of the JS engine on the machine /// - /// CLSID of JavaScript engine - /// Flag indicating whether this JavaScript engine is supported + /// CLSID of JS engine + /// Flag indicating whether this JS engine is supported /// Support synchronizer - /// Result of check (true - supports; false - does not support) - protected static bool IsSupported(string clsid, ref bool? isSupported, ref object supportSynchronizer) + /// Result of check (true - supports; false - does not support) + protected static bool IsSupported(string clsid, ref bool? isSupported, ref Lock supportSynchronizer) { if (isSupported.HasValue) { @@ -181,7 +233,7 @@ protected static bool IsSupported(string clsid, ref bool? isSupported, ref objec } catch (COMException e) { - if (e.ErrorCode == ComErrorCode.ClassNotRegistered) + if (e.ErrorCode == ComErrorCode.E_CLASS_NOT_REGISTERED) { isSupported = false; } @@ -204,87 +256,66 @@ protected static bool IsSupported(string clsid, ref bool? isSupported, ref objec } /// - /// Makes a mapping of value from the host type to a script type + /// Creates a instance of the Active Script wrapper /// - /// The source value - /// The mapped value - private object MapToScriptType(object value) + /// CLSID of JS engine + /// Version of script language + /// Instance of the Active Script wrapper + private IActiveScriptWrapper CreateActiveScriptWrapper(string clsid, ScriptLanguageVersion languageVersion) { - return TypeMappingHelpers.MapToScriptType(value, _engineMode); - } + IActiveScriptWrapper activeScriptWrapper; - /// - /// Makes a mapping of array items from the host type to a script type - /// - /// The source array - /// The mapped array - private object[] MapToScriptType(object[] args) - { - return TypeMappingHelpers.MapToScriptType(args, _engineMode); - } + if (Utils.Is64BitProcess()) + { + activeScriptWrapper = new ActiveScriptWrapper64(clsid, languageVersion, _settings.EnableDebugging); + } + else + { + activeScriptWrapper = new ActiveScriptWrapper32(clsid, languageVersion, _settings.EnableDebugging); + } - /// - /// Makes a mapping of value from the script type to a host type - /// - /// The source value - /// The mapped value - private object MapToHostType(object value) - { - return TypeMappingHelpers.MapToHostType(value); + return activeScriptWrapper; } /// - /// Makes a mapping of array items from the script type to a host type + /// Starts debugging /// - /// The source array - /// The mapped array - private object[] MapToHostType(object[] args) - { - return TypeMappingHelpers.MapToHostType(args); - } - - private JsRuntimeException ConvertActiveScriptExceptionToJsRuntimeException( - ActiveScriptException activeScriptException) + private void StartDebugging() { - var jsEngineException = new JsRuntimeException(activeScriptException.Message, _engineModeName) + if (ProcessDebugManagerWrapper.TryCreate(out _processDebugManagerWrapper)) { - ErrorCode = activeScriptException.ErrorCode.ToString(CultureInfo.InvariantCulture), - Category = activeScriptException.Subcategory, - LineNumber = (int)activeScriptException.LineNumber, - ColumnNumber = activeScriptException.ColumnNumber, - SourceFragment = activeScriptException.SourceError, - Source = activeScriptException.Source, - HelpLink = activeScriptException.HelpLink - }; + _processDebugManagerWrapper.CreateApplication(out _debugApplicationWrapper); - return jsEngineException; + if (_processDebugManagerWrapper.TryAddApplication(_debugApplicationWrapper, out _debugApplicationCookie)) + { + _debuggingStarted = true; + } + else + { + _debugApplicationWrapper.Close(); + _debugApplicationWrapper = null; + + _processDebugManagerWrapper = null; + } + } } /// - /// Initializes a script dispatch + /// Creates a instance of the Active Script site /// - private void InitScriptDispatch() - { - IExpando dispatch = null; - object obj; - - _activeScript.GetScriptDispatch(null, out obj); - - if (obj != null && obj.GetType().IsCOMObject) - { - dispatch = obj as IExpando; - } + /// Instance of the Active Script site + protected abstract ScriptSiteBase CreateScriptSite(); - if (dispatch == null) - { - throw new InvalidOperationException(Strings.Runtime_ActiveScriptDispatcherNotInitialized); - } - - _dispatch = dispatch; + /// + /// Initializes a script context + /// + protected virtual void InitScriptContext() + { + // Do nothing } /// - /// Gets and resets a last exception. Returns null for none. + /// Gets and resets a last exception. Returns null for none. /// private ActiveScriptException GetAndResetLastException() { @@ -297,53 +328,39 @@ private ActiveScriptException GetAndResetLastException() private void ThrowError() { ActiveScriptException last = GetAndResetLastException(); - if (last != null) + if (last is not null) { throw last; } } - private void InvokeScript(Action action) - { - try - { - _dispatcher.Invoke(DispatcherPriority.Input, action); - } - catch (ActiveScriptException e) - { - throw ConvertActiveScriptExceptionToJsRuntimeException(e); - } - catch (TargetInvocationException e) - { - var activeScriptException = e.InnerException as ActiveScriptException; - if (activeScriptException != null) - { - throw ConvertActiveScriptExceptionToJsRuntimeException(activeScriptException); - } - - throw; - } - } + /// + /// Gets a error type by number + /// + /// Error number + /// Error type + protected abstract string GetErrorTypeByNumber(int errorNumber); - private T InvokeScript(Func func) + /// + /// Loads a JS polyfills + /// + private void LoadPolyfills() { - try - { - return (T)_dispatcher.Invoke(DispatcherPriority.Input, func); - } - catch (ActiveScriptException e) + Assembly assembly = GetType() +#if !NET40 + .GetTypeInfo() +#endif + .Assembly + ; + + if (_settings.UseEcmaScript5Polyfill) { - throw ConvertActiveScriptExceptionToJsRuntimeException(e); + InnerExecuteResource(ES5_POLYFILL_RESOURCE_NAME, assembly); } - catch (TargetInvocationException e) - { - var activeScriptException = e.InnerException as ActiveScriptException; - if (activeScriptException != null) - { - throw ConvertActiveScriptExceptionToJsRuntimeException(activeScriptException); - } - throw; + if (_settings.UseJson2Library) + { + InnerExecuteResource(JSON2_LIBRARY_RESOURCE_NAME, assembly); } } @@ -351,16 +368,24 @@ private T InvokeScript(Func func) /// Executes a script text /// /// Script text + /// Document name /// Flag that script text needs to run as an expression /// Result of the execution - private object InnerExecute(string code, bool isExpression) + private object InnerExecute(string code, string documentName, bool isExpression) { object result; + DebugDocument debugDocument; + UIntPtr sourceContext; + ScriptTextFlags flags = isExpression ? ScriptTextFlags.IsExpression : ScriptTextFlags.IsVisible; + + if (TryCreateDebugDocument(documentName, code, out sourceContext, out debugDocument)) + { + flags |= ScriptTextFlags.HostManagesSource; + } try { - result = _activeScriptParse.ParseScriptText(code, null, null, null, IntPtr.Zero, - 0, isExpression ? ScriptTextFlags.IsExpression : ScriptTextFlags.IsVisible); + result = _activeScriptWrapper.ParseScriptText(code, null, null, null, sourceContext, 0, flags); } catch { @@ -374,6 +399,50 @@ private object InnerExecute(string code, bool isExpression) return result; } + /// + /// Executes a code from embedded JS resource + /// + /// The case-sensitive resource name + /// The assembly, which contains the embedded resource + private void InnerExecuteResource(string resourceName, Assembly assembly) + { + string code = Utils.GetResourceAsString(resourceName, assembly); + InnerExecute(code, resourceName, false); + } + + /// + /// Try create a debug document + /// + /// Document name + /// Script text + /// Application specific source context + /// Debug document + /// Result of creating a debug document (true - is created; false - is not created) + private bool TryCreateDebugDocument(string name, string code, out UIntPtr sourceContext, + out DebugDocument document) + { + bool result; + sourceContext = new UIntPtr(_nextSourceContext++); + document = null; + + if (_debuggingStarted) + { + document = new DebugDocument(_activeScriptWrapper, _debugApplicationWrapper, sourceContext, + name, code); + _debugDocuments[sourceContext] = document; + + result = true; + } + else + { + result = false; + } + + _documentNames[sourceContext] = name; + + return result; + } + /// /// Calls a function /// @@ -410,7 +479,7 @@ private object InnerGetVariableValue(string variableName) try { variableValue = _dispatch.InvokeMember(variableName, BindingFlags.GetProperty, - null, _dispatch, new object[0], null, + null, _dispatch, [], null, CultureInfo.InvariantCulture, null); } catch @@ -427,9 +496,9 @@ private object InnerGetVariableValue(string variableName) /// /// Name of variable /// Value of variable - private void InnerSetVariableValue(string variableName, object value) + protected void InnerSetVariableValue(string variableName, object value) { - object[] args = { value }; + object[] args = [value]; try { @@ -449,272 +518,267 @@ private void InnerSetVariableValue(string variableName, object value) } } - private void EmbedHostItem(string itemName, object value) + /// + /// Removes a variable + /// + /// Name of variable + protected abstract void InnerRemoveVariable(string variableName); + + private void InnerEmbedHostItem(string itemName, object value) { - InvokeScript(() => + object oldValue = null; + if (_hostItems.ContainsKey(itemName)) { - object oldValue = null; - if (_hostItems.ContainsKey(itemName)) - { - oldValue = _hostItems[itemName]; - } - _hostItems[itemName] = value; + oldValue = _hostItems[itemName]; + } + _hostItems[itemName] = value; - try + try + { + _activeScriptWrapper.AddNamedItem(itemName, ScriptItemFlags.IsVisible); + } + catch + { + if (oldValue is not null) { - _activeScript.AddNamedItem(itemName, ScriptItemFlags.IsVisible | ScriptItemFlags.GlobalMembers); + _hostItems[itemName] = oldValue; } - catch (Exception) + else { - if (oldValue != null) - { - _hostItems[itemName] = oldValue; - } - else - { - _hostItems.Remove(itemName); - } - - throw; + _hostItems.Remove(itemName); } - }); + + ThrowError(); + throw; + } } /// - /// Loads a resources + /// Starts a garbage collection /// - /// Flag for whether to use the ECMAScript 5 Polyfill - /// Flag for whether to use the JSON2 library - private void LoadResources(bool useEcmaScript5Polyfill, bool useJson2Library) + /// The type of garbage collection + private void InnerCollectGarbage(ScriptGCType type) { - Type type = GetType(); + _activeScriptWrapper.CollectGarbage(type); + } - if (useEcmaScript5Polyfill) - { - ExecuteResource(ES5_POLYFILL_RESOURCE_NAME, type); - } + #region Mapping - if (useJson2Library) - { - ExecuteResource(JSON2_LIBRARY_RESOURCE_NAME, type); - } + /// + /// Makes a mapping of value from the host type to a script type + /// + /// The source value + /// The mapped value + private object MapToScriptType(object value) + { + return TypeMappingHelpers.MapToScriptType(value, _settings.EngineMode, _settings.AllowReflection); + } + + /// + /// Makes a mapping of array items from the host type to a script type + /// + /// The source array + /// The mapped array + private object[] MapToScriptType(object[] args) + { + return TypeMappingHelpers.MapToScriptType(args, _settings.EngineMode, _settings.AllowReflection); } /// - /// Executes a code from embedded JS-resource + /// Makes a mapping of value from the script type to a host type /// - /// JS-resource name - /// Type from assembly that containing an embedded resource - private void ExecuteResource(string resourceName, Type type) + /// The source value + /// The mapped value + private object MapToHostType(object value) + { + return TypeMappingHelpers.MapToHostType(value); + } + + /// + /// Makes a mapping of array items from the script type to a host type + /// + /// The source array + /// The mapped array + private object[] MapToHostType(object[] args) + { + return TypeMappingHelpers.MapToHostType(args); + } + + private static IExpando WrapScriptDispatch(object dispatch) { - if (string.IsNullOrWhiteSpace(resourceName)) + IExpando wrappedDispatch = null; + if (dispatch is not null && dispatch.GetType().IsCOMObject) { - throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "resourceName"), "resourceName"); + wrappedDispatch = dispatch as IExpando; } - if (type == null) + if (wrappedDispatch is null) { - throw new ArgumentNullException( - "type", string.Format(Strings.Common_ArgumentIsNull, "type")); + throw new InvalidOperationException( + NetFrameworkStrings.Engine_ActiveScriptDispatcherNotInitialized); } - string code = Utils.GetResourceAsString(resourceName, type); - Execute(code); + return wrappedDispatch; } - /// - /// Destroys object - /// - /// Flag, allowing destruction of - /// managed objects contained in fields of class - private void Dispose(bool disposing) + private JsException WrapActiveScriptException(ActiveScriptException originalException) { - _dispatcher.Invoke(DispatcherPriority.Input, (Action)(() => - { - if (_disposedFlag.Set()) - { - if (_dispatch != null) - { - ComHelpers.ReleaseComObject(ref _dispatch, !disposing); - _dispatch = null; - } + JsException wrapperException; + string message = originalException.Message; + string category = originalException.Category; - if (_activeScriptParse != null) - { - _activeScriptParse.Dispose(); - _activeScriptParse = null; - } + switch (category) + { + case JsErrorCategory.Compilation: + wrapperException = new JsCompilationException(message, _engineModeName, originalException); + break; - if (_activeScript != null) + case JsErrorCategory.Runtime: + wrapperException = new JsRuntimeException(message, _engineModeName, originalException) { - _activeScript.Close(); - _activeScript = null; - } + CallStack = originalException.CallStack + }; + break; - ComHelpers.ReleaseAndEmpty(ref _pActiveScript); + case JsErrorCategory.Interrupted: + wrapperException = new JsInterruptedException(message, _engineModeName, originalException); + break; - if (_hostItems != null) - { - _hostItems.Clear(); - _hostItems = null; - } + default: + wrapperException = new JsException(message, _engineModeName, originalException); + break; + } - _lastException = null; - } - })); - } + wrapperException.Description = originalException.Description; - #region IActiveScriptSite implementation + var wrapperScriptException = wrapperException as JsScriptException; + if (wrapperScriptException is not null) + { + wrapperScriptException.Type = originalException.Type; + wrapperScriptException.DocumentName = originalException.DocumentName; + wrapperScriptException.LineNumber = (int)originalException.LineNumber; + wrapperScriptException.ColumnNumber = originalException.ColumnNumber; + wrapperScriptException.SourceFragment = originalException.SourceFragment; + } - /// - /// Retrieves the locale identifier associated with the host's user interface. The scripting - /// engine uses the identifier to ensure that error strings and other user-interface elements - /// generated by the engine appear in the appropriate language. - /// - /// A variable that receives the locale identifier for user-interface - /// elements displayed by the scripting engine - void IActiveScriptSite.GetLcid(out int lcid) - { - lcid = CultureInfo.CurrentCulture.LCID; + return wrapperException; } - /// - /// Allows the scripting engine to obtain information about an item added with the - /// IActiveScript.AddNamedItem method - /// - /// The name associated with the item, as specified in the - /// IActiveScript.AddNamedItem method - /// A bit mask specifying what information about the item should be - /// returned. The scripting engine should request the minimum amount of information possible - /// because some of the return parameters (for example, ITypeInfo) can take considerable - /// time to load or generate - /// A variable that receives a pointer to the IUnknown interface associated - /// with the given item. The scripting engine can use the IUnknown.QueryInterface method to - /// obtain the IDispatch interface for the item. This parameter receives null if mask - /// does not include the ScriptInfo.IUnknown value. Also, it receives null if there is no - /// object associated with the item name; this mechanism is used to create a simple class when - /// the named item was added with the ScriptItem.CodeOnly flag set in the - /// IActiveScript.AddNamedItem method. - /// A variable that receives a pointer to the ITypeInfo interface - /// associated with the item. This parameter receives null if mask does not include the - /// ScriptInfo.ITypeInfo value, or if type information is not available for this item. If type - /// information is not available, the object cannot source events, and name binding must be - /// realized with the IDispatch.GetIDsOfNames method. Note that the ITypeInfo interface - /// retrieved describes the item's coclass (TKIND_COCLASS) because the object may support - /// multiple interfaces and event interfaces. If the item supports the IProvideMultipleTypeInfo - /// interface, the ITypeInfo interface retrieved is the same as the index zero ITypeInfo that - /// would be obtained using the IProvideMultipleTypeInfo.GetInfoOfIndex method. - void IActiveScriptSite.GetItemInfo(string name, ScriptInfoFlags mask, ref IntPtr pUnkItem, ref IntPtr pTypeInfo) + private JsEngineLoadException WrapCOMException(COMException originalComException) { - object item = _hostItems[name]; - if (item == null) + string description; + string message; + + if (originalComException.ErrorCode == ComErrorCode.E_CLASS_NOT_REGISTERED) { - throw new COMException( - string.Format(Strings.Runtime_ItemNotFound, name), ComErrorCode.ElementNotFound); + description = string.Format(CommonStrings.Engine_AssemblyNotRegistered, + _settings.EngineMode == JsEngineMode.Classic ? DllName.JScript : DllName.JScript9) + + " " + + string.Format(CommonStrings.Engine_IeInstallationRequired, _lowerIeVersion) + ; + message = JsErrorHelpers.GenerateEngineLoadErrorMessage(description, _engineModeName); } - - if (mask.HasFlag(ScriptInfoFlags.IUnknown)) + else { - pUnkItem = Marshal.GetIDispatchForObject(item); + description = originalComException.Message; + message = JsErrorHelpers.GenerateEngineLoadErrorMessage(description, _engineModeName, true); } - if (mask.HasFlag(ScriptInfoFlags.ITypeInfo)) + var wrapperEngineLoadException = new JsEngineLoadException(message, _engineModeName, + originalComException) { - pTypeInfo = Marshal.GetITypeInfoForType(item.GetType()); - } - } + Description = description + }; - /// - /// Retrieves a host-defined string that uniquely identifies the current document version. If - /// the related document has changed outside the scope of Windows Script (as in the case of an - /// HTML page being edited with Notepad), the scripting engine can save this along with its - /// persisted state, forcing a recompile the next time the script is loaded. - /// - /// The host-defined document version string - void IActiveScriptSite.GetDocVersionString(out string version) - { - version = _documentVersion; + return wrapperEngineLoadException; } /// - /// Informs the host that the script has completed execution - /// - /// A variable that contains the script result, or null if the script - /// produced no result - /// Contains exception information generated when the script - /// terminated, or null if no exception was generated - void IActiveScriptSite.OnScriptTerminate(object result, EXCEPINFO exceptionInfo) - { } - - /// - /// Informs the host that the scripting engine has changed states - /// - /// Indicates the new script state - void IActiveScriptSite.OnStateChange(ScriptState scriptState) - { } - - /// - /// Informs the host that an execution error occurred while the engine was running the script. + /// Shortens a name of error category /// - /// A host can use this interface to obtain information about the - /// execution error - void IActiveScriptSite.OnScriptError(IActiveScriptError scriptError) + /// Name of error category + /// Short name of error category + private string ShortenErrorCategoryName(string categoryName) { - _lastException = ActiveScriptException.Create(scriptError); + return ActiveScriptJsErrorHelpers.ShortenErrorItemName(categoryName, _errorCategoryNamePrefix); } - /// - /// Informs the host that the scripting engine has begun executing the script code - /// - void IActiveScriptSite.OnEnterScript() - { } - - /// - /// Informs the host that the scripting engine has returned from executing script code - /// - void IActiveScriptSite.OnLeaveScript() - { } - #endregion + #region InnerJsEngineBase overrides + #region IInnerJsEngine implementation - public string Mode + public override PrecompiledScript Precompile(string code, string documentName) { - get { return _engineModeName; } + throw new NotSupportedException(); } - public object Evaluate(string expression) + public override object Evaluate(string expression, string documentName) { - object result = InvokeScript(() => InnerExecute(expression, true)); + object result = _dispatcher.Invoke(() => + { + InitScriptContext(); + + try + { + return InnerExecute(expression, documentName, true); + } + catch (ActiveScriptException e) + { + throw WrapActiveScriptException(e); + } + }); + result = MapToHostType(result); return result; } - public void Execute(string code) + public override void Execute(string code, string documentName) { - InvokeScript(() => + _dispatcher.Invoke(() => { - InnerExecute(code, false); + InitScriptContext(); + + try + { + InnerExecute(code, documentName, false); + } + catch (ActiveScriptException e) + { + throw WrapActiveScriptException(e); + } }); } - public object CallFunction(string functionName, params object[] args) + public override void Execute(PrecompiledScript precompiledScript) + { + throw new NotSupportedException(); + } + + public override object CallFunction(string functionName, params object[] args) { object[] processedArgs = MapToScriptType(args); - object result = InvokeScript(() => + object result = _dispatcher.Invoke(() => { + InitScriptContext(); + try { return InnerCallFunction(functionName, processedArgs); } + catch (ActiveScriptException e) + { + throw WrapActiveScriptException(e); + } catch (MissingMemberException) { throw new JsRuntimeException( - string.Format(Strings.Runtime_FunctionNotExist, functionName)); + string.Format(CommonStrings.Runtime_FunctionNotExist, functionName), + _engineModeName + ); } }); @@ -723,16 +787,22 @@ public object CallFunction(string functionName, params object[] args) return result; } - public bool HasVariable(string variableName) + public override bool HasVariable(string variableName) { - bool result = InvokeScript(() => + bool result = _dispatcher.Invoke(() => { + InitScriptContext(); + bool variableExist; try { object variableValue = InnerGetVariableValue(variableName); - variableExist = variableValue != null; + variableExist = variableValue is not null; + } + catch (ActiveScriptException e) + { + throw WrapActiveScriptException(e); } catch (MissingMemberException) { @@ -745,18 +815,26 @@ public bool HasVariable(string variableName) return result; } - public object GetVariableValue(string variableName) + public override object GetVariableValue(string variableName) { - object result = InvokeScript(() => + object result = _dispatcher.Invoke(() => { + InitScriptContext(); + try { return InnerGetVariableValue(variableName); } + catch (ActiveScriptException e) + { + throw WrapActiveScriptException(e); + } catch (MissingMemberException) { throw new JsRuntimeException( - string.Format(Strings.Runtime_VariableNotExist, variableName)); + string.Format(NetFrameworkStrings.Runtime_VariableNotExist, variableName), + _engineModeName + ); } }); @@ -765,35 +843,83 @@ public object GetVariableValue(string variableName) return result; } - public void SetVariableValue(string variableName, object value) + public override void SetVariableValue(string variableName, object value) { object processedValue = MapToScriptType(value); - InvokeScript(() => InnerSetVariableValue(variableName, processedValue)); + + _dispatcher.Invoke(() => + { + InitScriptContext(); + + try + { + InnerSetVariableValue(variableName, processedValue); + } + catch (ActiveScriptException e) + { + throw WrapActiveScriptException(e); + } + }); } - public void RemoveVariable(string variableName) + public override void RemoveVariable(string variableName) { - InvokeScript(() => + _dispatcher.Invoke(() => { - InnerSetVariableValue(variableName, null); + InitScriptContext(); - if (_hostItems.ContainsKey(variableName)) + try + { + InnerRemoveVariable(variableName); + } + catch (ActiveScriptException e) { - _hostItems.Remove(variableName); + throw WrapActiveScriptException(e); } }); } - public void EmbedHostObject(string itemName, object value) + public override void EmbedHostObject(string itemName, object value) { object processedValue = MapToScriptType(value); - EmbedHostItem(itemName, processedValue); + + _dispatcher.Invoke(() => + { + InitScriptContext(); + + try + { + InnerEmbedHostItem(itemName, processedValue); + } + catch (ActiveScriptException e) + { + throw WrapActiveScriptException(e); + } + }); } - public void EmbedHostType(string itemName, Type type) + public override void EmbedHostType(string itemName, Type type) { - var typeValue = new HostType(type, _engineMode); - EmbedHostItem(itemName, typeValue); + var typeValue = new HostType(type, _settings.EngineMode, _settings.AllowReflection); + + _dispatcher.Invoke(() => + { + InitScriptContext(); + + try + { + InnerEmbedHostItem(itemName, typeValue); + } + catch (ActiveScriptException e) + { + throw WrapActiveScriptException(e); + } + }); + } + + public override void CollectGarbage() + { + _dispatcher.Invoke(() => InnerCollectGarbage(ScriptGCType.Exhaustive)); } #endregion @@ -803,12 +929,69 @@ public void EmbedHostType(string itemName, Type type) /// /// Destroys object /// - public void Dispose() + public override void Dispose() { - Dispose(true /* disposing */); - GC.SuppressFinalize(this); + if (_disposedFlag.Set()) + { + if (_debuggingStarted && _debugDocuments is not null) + { + foreach (UIntPtr debugDocumentKey in _debugDocuments.Keys) + { + var debugDocumentValue = _debugDocuments[debugDocumentKey]; + debugDocumentValue.Close(); + } + + _debugDocuments.Clear(); + _debugDocuments = null; + } + + if (_processDebugManagerWrapper is not null) + { + _processDebugManagerWrapper.RemoveApplication(_debugApplicationCookie); + + if (_debugApplicationWrapper is not null) + { + _debugApplicationWrapper.Close(); + _debugApplicationWrapper = null; + } + + _processDebugManagerWrapper = null; + } + + if (_documentNames is not null) + { + _documentNames.Clear(); + _documentNames = null; + } + + _dispatcher.Invoke(() => + { + if (_dispatch is not null) + { + Marshal.ReleaseComObject(_dispatch); + _dispatch = null; + } + + if (_activeScriptWrapper is not null) + { + _activeScriptWrapper.Dispose(); + _activeScriptWrapper = null; + } + }); + + if (_hostItems is not null) + { + _hostItems.Clear(); + _hostItems = null; + } + + _lastException = null; + } } #endregion + + #endregion } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptJsErrorHelpers.cs b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptJsErrorHelpers.cs new file mode 100644 index 0000000..3cf8b1f --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptJsErrorHelpers.cs @@ -0,0 +1,102 @@ +#if NETFRAMEWORK +using System; +using System.Collections.Generic; + +using MsieJavaScriptEngine.Constants; +using MsieJavaScriptEngine.Extensions; + +namespace MsieJavaScriptEngine.ActiveScript +{ + /// + /// Active Script error helpers + /// + internal static class ActiveScriptJsErrorHelpers + { + /// + /// Checks whether the specified error number is compilation error + /// + /// Error number + /// Result of check (true - is compilation error; false - is not compilation error) + public static bool IsCompilationError(int errorNumber) + { + bool result = errorNumber >= JScriptSyntaxErrorNumber.SyntaxError + && errorNumber <= JScriptSyntaxErrorNumber.ThrowMustBeFollowedByExpressionOnSameSourceLine; + + return result; + } + + /// + /// Checks whether the specified error number is runtime error + /// + /// Error number + /// Result of check (true - is runtime error; false - is not runtime error) + public static bool IsRuntimeError(int errorNumber) + { bool result = errorNumber == JScriptRuntimeErrorNumber.OutOfStackSpace + || (errorNumber >= JScriptRuntimeErrorNumber.CannotAssignToThisKeyword + && errorNumber <= JScriptRuntimeErrorNumber.InvalidReplacerArgument); + + return result; + } + + /// + /// Gets a error type by number + /// + /// Error number + /// Mapping of error numbers and types + /// Error type + public static string GetErrorTypeByNumber(int errorNumber, Dictionary runtimeErrorTypeMap) + { + string errorType = string.Empty; + + if (IsCompilationError(errorNumber)) + { + errorType = JsErrorType.Syntax; + } + else if (IsRuntimeError(errorNumber)) + { + if (!runtimeErrorTypeMap.TryGetValue(errorNumber, out errorType)) + { + errorType = string.Empty; + } + } + + return errorType; + } + + /// + /// Shortens a name of error item + /// + /// Name of error item + /// Prefix + /// Short name of error item + public static string ShortenErrorItemName(string itemName, string prefix) + { + if (itemName is null) + { + throw new ArgumentNullException(nameof(itemName)); + } + + if (prefix is null) + { + throw new ArgumentNullException(nameof(prefix)); + } + + int itemNameLength = itemName.Length; + if (itemNameLength == 0 || prefix.Length == 0) + { + return itemName; + } + + string shortItemName = itemName.TrimStart(prefix); + int shortItemNameLength = shortItemName.Length; + + if (shortItemNameLength > 0 && shortItemNameLength < itemNameLength) + { + shortItemName = shortItemName.CapitalizeFirstLetter(); + } + + return shortItemName; + } + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptParseWrapper.cs b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptParseWrapper.cs deleted file mode 100644 index b3f7ac6..0000000 --- a/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptParseWrapper.cs +++ /dev/null @@ -1,308 +0,0 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - using System; - - using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; - - using Helpers; - using Resources; - - internal sealed class ActiveScriptParseWrapper : IActiveScriptParseWrapper - { - /// - /// Flag that the current process is a 64-bit process - /// - private readonly bool _is64Bit; - - /// - /// Pointer to an instance of 32-bit ActiveScript parser - /// - private IntPtr _pActiveScriptParse32 = IntPtr.Zero; - - /// - /// Instance of 32-bit ActiveScript parser - /// - private IActiveScriptParse32 _activeScriptParse32; - - /// - /// Pointer to an instance of 64-bit ActiveScript parser - /// - private IntPtr _pActiveScriptParse64 = IntPtr.Zero; - - /// - /// Instance of 64-bit ActiveScript parser - /// - private IActiveScriptParse64 _activeScriptParse64; - - /// - /// Last COM exception - /// - private EXCEPINFO _lastException; - - /// - /// Flag that object is destroyed - /// - private StatedFlag _disposedFlag = new StatedFlag(); - - /// - /// Gets a last COM exception - /// - public EXCEPINFO LastException - { - get { return _lastException; } - } - - - /// - /// Constructs instance of the class - /// - /// Pointer to an instance of native JavaScript engine - /// Instance of native JavaScript engine. - /// Must implement IActiveScriptParse32 or IActiveScriptParse64. - public ActiveScriptParseWrapper(IntPtr pActiveScript, IActiveScript activeScript) - { - _is64Bit = Environment.Is64BitProcess; - - if (_is64Bit) - { - _pActiveScriptParse64 = ComHelpers.QueryInterface(pActiveScript); - _activeScriptParse64 = activeScript as IActiveScriptParse64; - } - else - { - _pActiveScriptParse32 = ComHelpers.QueryInterface(pActiveScript); - _activeScriptParse32 = activeScript as IActiveScriptParse32; - } - - if (_activeScriptParse64 == null && _activeScriptParse32 == null) - { - throw new NotSupportedException(Strings.Runtime_InvalidParserImplementationError); - } - } - - /// - /// Destructs instance of - /// - ~ActiveScriptParseWrapper() - { - Dispose(false); - } - - - /// - /// Destroys object - /// - /// Flag, allowing destruction of - /// managed objects contained in fields of class - private void Dispose(bool disposing) - { - if (_disposedFlag.Set()) - { - if (_is64Bit) - { - _activeScriptParse64 = null; - ComHelpers.ReleaseAndEmpty(ref _pActiveScriptParse64); - } - else - { - _activeScriptParse32 = null; - ComHelpers.ReleaseAndEmpty(ref _pActiveScriptParse32); - } - } - } - - #region IActiveScriptParseWrapper implementation - - /// - /// Initializes the scripting engine - /// - public void InitNew() { - if (_is64Bit) - { - _activeScriptParse64.InitNew(); - } - else - { - _activeScriptParse32.InitNew(); - } - } - - /// - /// Adds a code scriptlet to the script. This method is used in environments where the - /// persistent state of the script is intertwined with the host document and the host - /// is responsible for restoring the script, rather than through an IPersist* interface. - /// The primary examples are HTML scripting languages that allow scriptlets of code - /// embedded in the HTML document to be attached to intrinsic events (for instance, - /// ONCLICK="button1.text='Exit'"). - /// - /// The default name to associate with the scriptlet. If the - /// scriptlet does not contain naming information (as in the ONCLICK example above), - /// this name will be used to identify the scriptlet. If this parameter is NULL, the - /// scripting engine manufactures a unique name, if necessary. - /// The scriptlet text to add. The interpretation of this string - /// depends on the scripting language. - /// The item name associated with this scriptlet. This parameter, - /// in addition to pstrSubItemName, identifies the object for which the scriptlet is - /// an event handler. - /// The name of a subobject of the named item with which this - /// scriptlet is associated; this name must be found in the named item's type - /// information. This parameter is NULL if the scriptlet is to be associated with the - /// named item instead of a subitem. This parameter, in addition to pstrItemName, - /// identifies the specific object for which the scriptlet is an event handler. - /// The name of the event for which the scriptlet is an event - /// handler. - /// The end-of-scriptlet delimiter. When the pstrCode parameter - /// is parsed from a stream of text, the host typically uses a delimiter, such as two - /// single quotation marks (''), to detect the end of the scriptlet. This parameter - /// specifies the delimiter that the host used, allowing the scripting engine to - /// provide some conditional primitive preprocessing (for example, replacing a single - /// quotation mark ['] with two single quotation marks for use as a delimiter). - /// Exactly how (and if) the scripting engine makes use of this information depends - /// on the scripting engine. Set this parameter to NULL if the host did not use a - /// delimiter to mark the end of the scriptlet. - /// Application-defined value that is used for - /// debugging purposes - /// Zero-based value that specifies which line the - /// parsing will begin at - /// Flags associated with the scriptlet - /// - /// Actual name used to identify the scriptlet. This is to be in - /// order of preference: a name explicitly specified in the scriptlet text, the - /// default name provided in pstrDefaultName, or a unique name synthesized by the - /// scripting engine. - /// - public string AddScriptlet( - string defaultName, - string code, - string itemName, - string subItemName, - string eventName, - string delimiter, - IntPtr sourceContextCookie, - uint startingLineNumber, - ScriptTextFlags flags) { - - string name; - - if (_is64Bit) - { - _activeScriptParse64.AddScriptlet( - defaultName, - code, - itemName, - subItemName, - eventName, - delimiter, - sourceContextCookie, - startingLineNumber, - flags, - out name, - out _lastException); - } - else - { - _activeScriptParse32.AddScriptlet( - defaultName, - code, - itemName, - subItemName, - eventName, - delimiter, - sourceContextCookie, - startingLineNumber, - flags, - out name, - out _lastException); - } - - return name; - } - - /// - /// Parses the given code scriptlet, adding declarations into the namespace and - /// evaluating code as appropriate. - /// - /// The scriptlet text to evaluate. The interpretation of this - /// string depends on the scripting language - /// The item name that gives the context in which the - /// scriptlet is to be evaluated. If this parameter is NULL, the code is evaluated - /// in the scripting engine's global context - /// The context object. This object is reserved for use in a - /// debugging environment, where such a context may be provided by the debugger to - /// represent an active run-time context. If this parameter is NULL, the engine - /// uses pstrItemName to identify the context. - /// The end-of-scriptlet delimiter. When pstrCode is parsed - /// from a stream of text, the host typically uses a delimiter, such as two single - /// quotation marks (''), to detect the end of the scriptlet. This parameter specifies - /// the delimiter that the host used, allowing the scripting engine to provide some - /// conditional primitive preprocessing (for example, replacing a single quotation - /// mark ['] with two single quotation marks for use as a delimiter). Exactly how - /// (and if) the scripting engine makes use of this information depends on the - /// scripting engine. Set this parameter to NULL if the host did not use a delimiter - /// to mark the end of the scriptlet. - /// Application-defined value that is used for - /// debugging purposes - /// Zero-based value that specifies which line the - /// parsing will begin at - /// Flags associated with the scriptlet - /// - /// The results of scriptlet processing, or NULL if the caller - /// expects no result (that is, the SCRIPTTEXT_ISEXPRESSION value is not set). - /// - public object ParseScriptText( - string code, - string itemName, - object context, - string delimiter, - IntPtr sourceContextCookie, - uint startingLineNumber, - ScriptTextFlags flags) { - - object result; - - if (_is64Bit) - { - _activeScriptParse64.ParseScriptText( - code, - itemName, - context, - delimiter, - sourceContextCookie, - startingLineNumber, - flags, - out result, - out _lastException); - } - else - { - _activeScriptParse32.ParseScriptText( - code, - itemName, - context, - delimiter, - sourceContextCookie, - startingLineNumber, - flags, - out result, - out _lastException); - } - - return result; - } - - #endregion - - #region IDisposable implementation - - /// - /// Destroys object - /// - public void Dispose() - { - Dispose(true /* disposing */); - GC.SuppressFinalize(this); - } - - #endregion - } -} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptWrapper32.cs b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptWrapper32.cs new file mode 100644 index 0000000..9239d76 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptWrapper32.cs @@ -0,0 +1,179 @@ +#if NETFRAMEWORK +using System; + +using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; + +using MsieJavaScriptEngine.ActiveScript.Debugging; +using MsieJavaScriptEngine.Helpers; + +namespace MsieJavaScriptEngine.ActiveScript +{ + /// + /// 32-bit Active Script wrapper + /// + internal sealed class ActiveScriptWrapper32 : ActiveScriptWrapperBase + { + /// + /// Pointer to an instance of 32-bit Active Script parser + /// + private IntPtr _pActiveScriptParse32; + + /// + /// Pointer to an instance of 32-bit Active Script debugger + /// + private IntPtr _pActiveScriptDebug32; + + /// + /// Pointer to an instance of 32-bit debug stack frame sniffer + /// + private IntPtr _pDebugStackFrameSniffer32; + + /// + /// Instance of 32-bit Active Script parser + /// + private IActiveScriptParse32 _activeScriptParse32; + + /// + /// Instance of 32-bit Active Script debugger + /// + private IActiveScriptDebug32 _activeScriptDebug32; + + /// + /// Instance of 32-bit debug stack frame sniffer + /// + private IDebugStackFrameSnifferEx32 _debugStackFrameSniffer32; + + + /// + /// Constructs an instance of the 32-bit Active Script wrapper + /// + /// CLSID of JS engine + /// Version of script language + /// Flag for whether to enable script debugging features + public ActiveScriptWrapper32(string clsid, ScriptLanguageVersion languageVersion, bool enableDebugging) + : base(clsid, languageVersion, enableDebugging) + { + _pActiveScriptParse32 = ComHelpers.QueryInterface(_pActiveScript); + _activeScriptParse32 = (IActiveScriptParse32)_activeScript; + + if (_enableDebugging) + { + _pActiveScriptDebug32 = ComHelpers.QueryInterface(_pActiveScript); + _activeScriptDebug32 = (IActiveScriptDebug32)_activeScript; + + _pDebugStackFrameSniffer32 = ComHelpers.QueryInterfaceNoThrow( + _pActiveScript); + _debugStackFrameSniffer32 = _activeScript as IDebugStackFrameSnifferEx32; + } + } + + /// + /// Destructs an instance of the 32-bit Active Script wrapper + /// + ~ActiveScriptWrapper32() + { + Dispose(false); + } + + + #region ActiveScriptWrapperBase overrides + + protected override uint InnerEnumCodeContextsOfPosition(UIntPtr sourceContext, uint offset, + uint length, out IEnumDebugCodeContexts enumContexts) + { + uint result = _activeScriptDebug32.EnumCodeContextsOfPosition(sourceContext.ToUInt32(), + offset, length, out enumContexts); + + return result; + } + + #region IActiveScriptWrapper implementation + + public override void InitNew() + { + _activeScriptParse32.InitNew(); + } + + public override object ParseScriptText(string code, string itemName, object context, + string delimiter, UIntPtr sourceContextCookie, uint startingLineNumber, ScriptTextFlags flags) + { + object result; + EXCEPINFO exceptionInfo; + + _activeScriptParse32.ParseScriptText( + code, + itemName, + context, + delimiter, + sourceContextCookie, + startingLineNumber, + flags, + out result, + out exceptionInfo); + + return result; + } + + public override void EnumStackFrames(out IEnumDebugStackFrames enumFrames) + { + if (_debugStackFrameSniffer32 is not null) + { + _debugStackFrameSniffer32.EnumStackFrames(out enumFrames); + } + else + { + enumFrames = new NullEnumDebugStackFrames(); + } + } + + #endregion + + #region IDisposable implementation + + /// + /// Destroys object + /// + public override void Dispose() + { + Dispose(true /* disposing */); + GC.SuppressFinalize(this); + } + + /// + /// Destroys object + /// + /// Flag, allowing destruction of managed objects contained in fields of class + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (_disposedFlag.Set()) + { + _debugStackFrameSniffer32 = null; + _activeScriptDebug32 = null; + _activeScriptParse32 = null; + + DisposeUnmanagedResources(); + base.Dispose(true); + } + } + else + { + DisposeUnmanagedResources(); + base.Dispose(false); + } + } + + private void DisposeUnmanagedResources() + { + ComHelpers.ReleaseAndEmpty(ref _pDebugStackFrameSniffer32); + ComHelpers.ReleaseAndEmpty(ref _pActiveScriptDebug32); + ComHelpers.ReleaseAndEmpty(ref _pActiveScriptParse32); + } + + #endregion + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptWrapper64.cs b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptWrapper64.cs new file mode 100644 index 0000000..bff66b4 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptWrapper64.cs @@ -0,0 +1,179 @@ +#if NETFRAMEWORK +using System; + +using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; + +using MsieJavaScriptEngine.ActiveScript.Debugging; +using MsieJavaScriptEngine.Helpers; + +namespace MsieJavaScriptEngine.ActiveScript +{ + /// + /// 64-bit Active Script wrapper + /// + internal sealed class ActiveScriptWrapper64 : ActiveScriptWrapperBase + { + /// + /// Pointer to an instance of 64-bit Active Script parser + /// + private IntPtr _pActiveScriptParse64; + + /// + /// Pointer to an instance of 64-bit Active Script debugger + /// + private IntPtr _pActiveScriptDebug64; + + /// + /// Pointer to an instance of 64-bit debug stack frame sniffer + /// + private IntPtr _pDebugStackFrameSniffer64; + + /// + /// Instance of 64-bit Active Script parser + /// + private IActiveScriptParse64 _activeScriptParse64; + + /// + /// Instance of 64-bit Active Script debugger + /// + private IActiveScriptDebug64 _activeScriptDebug64; + + /// + /// Instance of 64-bit debug stack frame sniffer + /// + private IDebugStackFrameSnifferEx64 _debugStackFrameSniffer64; + + + /// + /// Constructs an instance of the 64-bit Active Script wrapper + /// + /// CLSID of JS engine + /// Version of script language + /// Flag for whether to enable script debugging features + public ActiveScriptWrapper64(string clsid, ScriptLanguageVersion languageVersion, bool enableDebugging) + : base(clsid, languageVersion, enableDebugging) + { + _pActiveScriptParse64 = ComHelpers.QueryInterface(_pActiveScript); + _activeScriptParse64 = (IActiveScriptParse64)_activeScript; + + if (_enableDebugging) + { + _pActiveScriptDebug64 = ComHelpers.QueryInterface(_pActiveScript); + _activeScriptDebug64 = (IActiveScriptDebug64)_activeScript; + + _pDebugStackFrameSniffer64 = ComHelpers.QueryInterfaceNoThrow( + _pActiveScript); + _debugStackFrameSniffer64 = _activeScript as IDebugStackFrameSnifferEx64; + } + } + + /// + /// Destructs an instance of the 64-bit Active Script wrapper + /// + ~ActiveScriptWrapper64() + { + Dispose(false); + } + + + #region ActiveScriptWrapperBase overrides + + protected override uint InnerEnumCodeContextsOfPosition(UIntPtr sourceContext, uint offset, + uint length, out IEnumDebugCodeContexts enumContexts) + { + uint result = _activeScriptDebug64.EnumCodeContextsOfPosition(sourceContext.ToUInt64(), + offset, length, out enumContexts); + + return result; + } + + #region IActiveScriptWrapper implementation + + public override void InitNew() + { + _activeScriptParse64.InitNew(); + } + + public override object ParseScriptText(string code, string itemName, object context, + string delimiter, UIntPtr sourceContextCookie, uint startingLineNumber, ScriptTextFlags flags) + { + object result; + EXCEPINFO exceptionInfo; + + _activeScriptParse64.ParseScriptText( + code, + itemName, + context, + delimiter, + sourceContextCookie, + startingLineNumber, + flags, + out result, + out exceptionInfo); + + return result; + } + + public override void EnumStackFrames(out IEnumDebugStackFrames enumFrames) + { + if (_debugStackFrameSniffer64 is not null) + { + _debugStackFrameSniffer64.EnumStackFrames(out enumFrames); + } + else + { + enumFrames = new NullEnumDebugStackFrames(); + } + } + + #endregion + + #region IDisposable implementation + + /// + /// Destroys object + /// + public override void Dispose() + { + Dispose(true /* disposing */); + GC.SuppressFinalize(this); + } + + /// + /// Destroys object + /// + /// Flag, allowing destruction of managed objects contained in fields of class + protected override void Dispose(bool disposing) + { + if (disposing) + { + if (_disposedFlag.Set()) + { + _debugStackFrameSniffer64 = null; + _activeScriptDebug64 = null; + _activeScriptParse64 = null; + + DisposeUnmanagedResources(); + base.Dispose(true); + } + } + else + { + DisposeUnmanagedResources(); + base.Dispose(false); + } + } + + private void DisposeUnmanagedResources() + { + ComHelpers.ReleaseAndEmpty(ref _pDebugStackFrameSniffer64); + ComHelpers.ReleaseAndEmpty(ref _pActiveScriptDebug64); + ComHelpers.ReleaseAndEmpty(ref _pActiveScriptParse64); + } + + #endregion + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptWrapperBase.cs b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptWrapperBase.cs new file mode 100644 index 0000000..dc9bb3f --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/ActiveScriptWrapperBase.cs @@ -0,0 +1,265 @@ +#if NETFRAMEWORK +using System; +using System.Runtime.InteropServices; + +using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; + +using MsieJavaScriptEngine.ActiveScript.Debugging; +using MsieJavaScriptEngine.Constants; +using MsieJavaScriptEngine.Helpers; +using MsieJavaScriptEngine.Resources; +using MsieJavaScriptEngine.Utilities; + +namespace MsieJavaScriptEngine.ActiveScript +{ + /// + /// Base class of the Active Script wrapper + /// + internal abstract class ActiveScriptWrapperBase : IActiveScriptWrapper + { + /// + /// Flag for whether to enable script debugging features + /// + protected readonly bool _enableDebugging; + + /// + /// Pointer to an instance of Active Script engine + /// + protected IntPtr _pActiveScript; + + /// + /// Pointer to an instance of Active Script garbage collector + /// + private IntPtr _pActiveScriptGarbageCollector; + + /// + /// Instance of Active Script engine + /// + protected IActiveScript _activeScript; + + /// + /// Instance of Active Script garbage collector + /// + private IActiveScriptGarbageCollector _activeScriptGarbageCollector; + + /// + /// Flag that object is destroyed + /// + protected StatedFlag _disposedFlag = new StatedFlag(); + + + /// + /// Constructs an instance of the Active Script wrapper + /// + /// CLSID of JS engine + /// Version of script language + /// Flag for whether to enable script debugging features + protected ActiveScriptWrapperBase(string clsid, ScriptLanguageVersion languageVersion, + bool enableDebugging) + { + _enableDebugging = enableDebugging; + + _pActiveScript = ComHelpers.CreateInstanceByClsid(clsid); + _activeScript = (IActiveScript)Marshal.GetObjectForIUnknown(_pActiveScript); + + _pActiveScriptGarbageCollector = ComHelpers.QueryInterfaceNoThrow( + _pActiveScript); + _activeScriptGarbageCollector = _activeScript as IActiveScriptGarbageCollector; + + if (languageVersion != ScriptLanguageVersion.None) + { + var activeScriptProperty = _activeScript as IActiveScriptProperty; + if (activeScriptProperty is not null) + { + object scriptLanguageVersion = (int)languageVersion; + uint result = activeScriptProperty.SetProperty((uint)ScriptProperty.InvokeVersioning, + IntPtr.Zero, ref scriptLanguageVersion); + if (result != ComErrorCode.S_OK) + { + throw new InvalidOperationException( + string.Format(NetFrameworkStrings.Engine_ActiveScriptLanguageVersionSelectionFailed, + languageVersion) + ); + } + } + } + } + + + protected abstract uint InnerEnumCodeContextsOfPosition(UIntPtr sourceContext, uint offset, + uint length, out IEnumDebugCodeContexts enumContexts); + + #region IActiveScriptWrapper implementation + + /// + /// Informs the scripting engine of the interface site + /// provided by the host. Call this method before any other + /// interface methods is used. + /// + /// The host-supplied script site to be associated with this instance + /// of the scripting engine. The site must be uniquely assigned to this scripting engine + /// instance; it cannot be shared with other scripting engines. + public void SetScriptSite(IActiveScriptSite site) + { + _activeScript.SetScriptSite(site); + } + + /// + /// Puts the scripting engine into the given state. This method can be called from non-base + /// threads without resulting in a non-base callout to host objects or to the + /// interface. + /// + /// Sets the scripting engine to the given state + public void SetScriptState(ScriptState state) + { + _activeScript.SetScriptState(state); + } + + /// + /// Adds the name of a root-level item to the scripting engine's name space. A root-level item + /// is an object with properties and methods, an event source, or all three. + /// + /// The name of the item as viewed from the script. The name must be unique + /// and persistable + /// Flags associated with an item + public void AddNamedItem(string name, ScriptItemFlags flags) + { + _activeScript.AddNamedItem(name, flags); + } + + /// + /// Gets a script dispatch + /// + /// The object associated with the script's global methods and properties + public object GetScriptDispatch() + { + object dispatch; + _activeScript.GetScriptDispatch(null, out dispatch); + + return dispatch; + } + + /// + /// Initializes the scripting engine + /// + public abstract void InitNew(); + + /// + /// Parses the given code scriptlet, adding declarations into the namespace and + /// evaluating code as appropriate + /// + /// The scriptlet text to evaluate. The interpretation of this + /// string depends on the scripting language + /// The item name that gives the context in which the + /// scriptlet is to be evaluated. If this parameter is null, the code is evaluated + /// in the scripting engine's global context + /// The context object. This object is reserved for use in a + /// debugging environment, where such a context may be provided by the debugger to + /// represent an active run-time context. If this parameter is null, the engine + /// uses to identify the context. + /// The end-of-scriptlet delimiter. When + /// is parsed from a stream of text, the host typically uses a delimiter, such as two + /// single quotation marks (''), to detect the end of the scriptlet. This parameter + /// specifies the delimiter that the host used, allowing the scripting engine to provide + /// some conditional primitive preprocessing (for example, replacing a single quotation + /// mark ['] with two single quotation marks for use as a delimiter). Exactly how + /// (and if) the scripting engine makes use of this information depends on the + /// scripting engine. Set this parameter to null if the host did not use a delimiter + /// to mark the end of the scriptlet. + /// Application-defined value that is used for + /// debugging purposes + /// Zero-based value that specifies which line the + /// parsing will begin at + /// Flags associated with the scriptlet + /// The results of scriptlet processing, or null if the caller expects no + /// result (that is, the value is not set) + public abstract object ParseScriptText(string code, string itemName, object context, string delimiter, + UIntPtr sourceContextCookie, uint startingLineNumber, ScriptTextFlags flags); + + /// + /// Used by a smart host to delegate the method + /// + /// The source context as provided to + /// or + /// + /// Character offset relative to start of script text + /// Number of characters in this context + /// An enumerator of the code contexts in the specified range + public void EnumCodeContextsOfPosition(UIntPtr sourceContext, uint offset, uint length, + out IEnumDebugCodeContexts enumContexts) + { + if (_enableDebugging) + { + uint result = InnerEnumCodeContextsOfPosition(sourceContext, offset, length, out enumContexts); + ComHelpers.HResult.Check(result); + } + else + { + throw new InvalidOperationException(); + } + } + + public abstract void EnumStackFrames(out IEnumDebugStackFrames enumFrames); + + /// + /// Interrupts the execution of a running script thread (an event sink, an immediate execution, + /// or a macro invocation). This method can be used to terminate a script that is stuck (for + /// example, in an infinite loop). It can be called from non-base threads without resulting in + /// a non-base callout to host objects or to the method. + /// + /// Identifier of the thread to interrupt + /// The error information that should be reported to the aborted script + /// Option flags associated with the interruption + public void InterruptScriptThread(uint scriptThreadId, ref EXCEPINFO exceptionInfo, + ScriptInterruptFlags flags) + { + _activeScript.InterruptScriptThread(scriptThreadId, ref exceptionInfo, flags); + } + + /// + /// The Active Script host calls this method to start garbage collection + /// + /// The type of garbage collection + public void CollectGarbage(ScriptGCType type) + { + if (_activeScriptGarbageCollector is not null) + { + _activeScriptGarbageCollector.CollectGarbage(type); + } + } + + #endregion + + #region IDisposable implementation + + /// + /// Destroys object + /// + public abstract void Dispose(); + + /// + /// Destroys object + /// + /// Flag, allowing destruction of managed objects contained in fields of class + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _activeScriptGarbageCollector = null; + + if (_activeScript is not null) + { + _activeScript.Close(); + Marshal.FinalReleaseComObject(_activeScript); + _activeScript = null; + } + } + + ComHelpers.ReleaseAndEmpty(ref _pActiveScriptGarbageCollector); + ComHelpers.ReleaseAndEmpty(ref _pActiveScript); + } + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ChakraActiveScriptJsEngine.ScriptSite.cs b/src/MsieJavaScriptEngine/ActiveScript/ChakraActiveScriptJsEngine.ScriptSite.cs new file mode 100644 index 0000000..7a4fcb3 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/ChakraActiveScriptJsEngine.ScriptSite.cs @@ -0,0 +1,58 @@ +#if NETFRAMEWORK +using MsieJavaScriptEngine.Constants; +using MsieJavaScriptEngine.Helpers; +using MsieJavaScriptEngine.Resources; + +namespace MsieJavaScriptEngine.ActiveScript +{ + internal sealed partial class ChakraActiveScriptJsEngine + { + /// + /// Chakra Active Script site + /// + private sealed class ScriptSite : ScriptSiteBase, IActiveScriptSiteInterruptPoll + { + /// + /// Constructs an instance of the Chakra Active Script site + /// + /// Instance of the Active Script JS engine + public ScriptSite(ChakraActiveScriptJsEngine jsEngine) + : base(jsEngine) + { } + + + #region IActiveScriptSiteInterruptPoll implementation + + public uint QueryContinue() + { + int hResult; + + if (InterruptRequested) + { + hResult = ComErrorCode.E_ABORT; + string category = JsErrorCategory.Interrupted; + string description = CommonStrings.Runtime_ScriptInterrupted; + string message = description; + + var activeScriptException = new ActiveScriptException(message) + { + ErrorCode = hResult, + Category = category, + Description = description + }; + + LastException = activeScriptException; + } + else + { + hResult = ComErrorCode.S_OK; + } + + return NumericHelpers.SignedAsUnsigned(hResult); + } + + #endregion + } + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ChakraActiveScriptJsEngine.cs b/src/MsieJavaScriptEngine/ActiveScript/ChakraActiveScriptJsEngine.cs index 54a2829..1240edb 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/ChakraActiveScriptJsEngine.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/ChakraActiveScriptJsEngine.cs @@ -1,43 +1,140 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - using Constants; +#if NETFRAMEWORK +using System.Collections.Generic; + +using MsieJavaScriptEngine.Constants; +namespace MsieJavaScriptEngine.ActiveScript +{ /// - /// ActiveScript version of Chakra JavaScript engine + /// Active Script version of Chakra JS engine /// - internal sealed class ChakraActiveScriptJsEngine : ActiveScriptJsEngineBase + internal sealed partial class ChakraActiveScriptJsEngine : ActiveScriptJsEngineBase { - private const string CHAKRA_CLSID = "{16d51579-a30b-4c8b-a276-0ff4dc41e755}"; - /// - /// Flag indicating whether this JavaScript engine is supported + /// Flag indicating whether this JS engine is supported /// private static bool? _isSupported; /// /// Support synchronizer /// - private static object _supportSynchronizer = new object(); + private static Lock _supportSynchronizer = new Lock(); + + /// + /// Mapping of error numbers and types + /// + private static readonly Dictionary _runtimeErrorTypeMap = new Dictionary + { + { JScriptRuntimeErrorNumber.OutOfStackSpace, JsErrorType.Common }, + { JScriptRuntimeErrorNumber.CannotAssignToThisKeyword, JsErrorType.Reference }, + { JScriptRuntimeErrorNumber.NumberExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.FunctionExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.CannotAssignToFunctionResult, JsErrorType.Reference }, + { JScriptRuntimeErrorNumber.StringExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.DateObjectExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.ObjectExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.IllegalAssignment, string.Empty }, + { JScriptRuntimeErrorNumber.UndefinedIdentifier, JsErrorType.Reference }, + { JScriptRuntimeErrorNumber.BooleanExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.ObjectMemberExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.VbArrayExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.JavaScriptObjectExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.EnumeratorObjectExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.RegularExpressionObjectExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.SyntaxErrorInRegularExpression, JsErrorType.Syntax }, + { JScriptRuntimeErrorNumber.UnexpectedQuantifier, JsErrorType.Syntax }, + { JScriptRuntimeErrorNumber.ExpectedRightSquareBracketInRegularExpression, JsErrorType.Syntax }, + { JScriptRuntimeErrorNumber.ExpectedRightParenthesisInRegularExpression, JsErrorType.Syntax }, + { JScriptRuntimeErrorNumber.InvalidRangeInCharacterSet, JsErrorType.Syntax }, + { JScriptRuntimeErrorNumber.ExceptionThrownAndNotCaught, string.Empty }, + { JScriptRuntimeErrorNumber.FunctionDoesNotHaveValidPrototypeObject, string.Empty }, + { JScriptRuntimeErrorNumber.UriToBeEncodedContainsInvalidCharacter, JsErrorType.URI }, + { JScriptRuntimeErrorNumber.UriToBeDecodedIsNotValidEncoding, JsErrorType.URI }, + { JScriptRuntimeErrorNumber.NumberOfFractionalDigitsIsOutOfRange, JsErrorType.Range }, + { JScriptRuntimeErrorNumber.PrecisionOutOfRange, JsErrorType.Range }, + { JScriptRuntimeErrorNumber.ArrayOrArgumentsObjectExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.ArrayLengthMustBeFinitePositiveInteger, JsErrorType.Range }, + { JScriptRuntimeErrorNumber.ArrayLengthMustBeAssignedFinitePositiveNumber, JsErrorType.Range }, + { JScriptRuntimeErrorNumber.CircularReferenceInValueArgumentNotSupported, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.InvalidReplacerArgument, string.Empty } + }; /// - /// Constructs instance of the Chakra ActiveScript JavaScript engine + /// Constructs an instance of the Chakra Active Script engine /// - public ChakraActiveScriptJsEngine() - : base(CHAKRA_CLSID, JsEngineMode.ChakraActiveScript, "9", - ScriptLanguageVersion.EcmaScript5, false, false) + /// JS engine settings + public ChakraActiveScriptJsEngine(JsEngineSettings settings) + : base(settings, ClassId.Chakra, ScriptLanguageVersion.EcmaScript5, "9", "JavaScript ") { } /// - /// Checks a support of the Chakra ActiveScript JavaScript engine on the machine + /// Checks a support of the Chakra Active Script engine on the machine /// - /// Result of check (true - supports; false - does not support) + /// Result of check (true - supports; false - does not support) public static bool IsSupported() { - bool isSupported = IsSupported(CHAKRA_CLSID, ref _isSupported, ref _supportSynchronizer); + bool isSupported = IsSupported(ClassId.Chakra, ref _isSupported, ref _supportSynchronizer); return isSupported; } + + #region ActiveScriptJsEngineBase overrides + + /// + /// Creates a instance of the Active Script site + /// + /// Instance of the Active Script site + protected override ScriptSiteBase CreateScriptSite() + { + return new ScriptSite(this); + } + + /// + /// Initializes a script context + /// + protected override void InitScriptContext() + { + _interruptRequested = false; + } + + /// + /// Gets a error type by number + /// + /// Error number + /// Error type + protected override string GetErrorTypeByNumber(int errorNumber) + { + return ActiveScriptJsErrorHelpers.GetErrorTypeByNumber(errorNumber, _runtimeErrorTypeMap); + } + + protected override void InnerRemoveVariable(string variableName) + { + InnerSetVariableValue(variableName, null); + + if (_hostItems.ContainsKey(variableName)) + { + _hostItems.Remove(variableName); + } + } + + #region IInnerJsEngine implementation + + public override bool SupportsScriptPrecompilation + { + get { return false; } + } + + + public override void Interrupt() + { + _interruptRequested = true; + } + + #endregion + + #endregion } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ClassicActiveScriptJsEngine.ScriptSite.cs b/src/MsieJavaScriptEngine/ActiveScript/ClassicActiveScriptJsEngine.ScriptSite.cs new file mode 100644 index 0000000..0c5143f --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/ClassicActiveScriptJsEngine.ScriptSite.cs @@ -0,0 +1,141 @@ +#if NETFRAMEWORK +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +using MsieJavaScriptEngine.ActiveScript.Debugging; +using MsieJavaScriptEngine.Constants; +using MsieJavaScriptEngine.Helpers; +using MsieJavaScriptEngine.Utilities; + +namespace MsieJavaScriptEngine.ActiveScript +{ + internal sealed partial class ClassicActiveScriptJsEngine + { + /// + /// Classic Active Script site + /// + private sealed class ScriptSite : ScriptSiteBase + { + /// + /// Constructs an instance of the Classic Active Script site + /// + /// Instance of the Active Script JS engine + public ScriptSite(ActiveScriptJsEngineBase jsEngine) + : base(jsEngine) + { } + + + #region ScriptSiteBase overrides + + /// + /// Gets a flag that indicates if the script interruption is requested + /// + public override bool InterruptRequested + { + get { return false; } + } + + + /// + /// Processes a Active Script error + /// + /// Instance of + protected override void ProcessActiveScriptError(IActiveScriptError error) + { + var activeScriptException = CreateActiveScriptException(error); + if (activeScriptException.ErrorCode == ComErrorCode.E_ABORT) + { + // Script execution was interrupted explicitly. At this point the script + // engine might be in an odd state; the following call seems to get it back + // to normal. + ActiveScriptWrapper.SetScriptState(ScriptState.Started); + } + + LastException = activeScriptException; + } + + /// + /// Gets a array of instances + /// + /// An array of instances + protected override CallStackItem[] GetCallStackItems() + { + var callStackItems = new List(); + IEnumDebugStackFrames enumFrames; + ActiveScriptWrapper.EnumStackFrames(out enumFrames); + + while (true) + { + DebugStackFrameDescriptor descriptor; + uint countFetched; + enumFrames.Next(1, out descriptor, out countFetched); + if (countFetched < 1) + { + break; + } + + try + { + IDebugStackFrame stackFrame = descriptor.Frame; + + string functionName; + stackFrame.GetDescriptionString(true, out functionName); + + string shortFunctionName = ActiveScriptJsErrorHelpers.ShortenErrorItemName( + functionName, "JScript "); + + IDebugCodeContext codeContext; + stackFrame.GetCodeContext(out codeContext); + + IDebugDocumentContext documentContext; + codeContext.GetDocumentContext(out documentContext); + + string documentName = string.Empty; + uint lineNumber = 0; + uint columnNumber = 0; + + if (documentContext is not null) + { + IDebugDocument document; + documentContext.GetDocument(out document); + + document.GetName(DocumentNameType.Title, out documentName); + + var documentText = (IDebugDocumentText)document; + + uint position; + uint length; + documentText.GetPositionOfContext(documentContext, out position, out length); + + uint offsetInLine; + documentText.GetLineOfPosition(position, out lineNumber, out offsetInLine); + columnNumber = offsetInLine + 1; + } + + CallStackItem callStackItem = new CallStackItem + { + FunctionName = shortFunctionName, + DocumentName = documentName, + LineNumber = (int)lineNumber, + ColumnNumber = (int)columnNumber + }; + callStackItems.Add(callStackItem); + } + finally + { + if (descriptor.pFinalObject != IntPtr.Zero) + { + Marshal.Release(descriptor.pFinalObject); + } + } + } + + return callStackItems.ToArray(); + } + + #endregion + } + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ClassicActiveScriptJsEngine.cs b/src/MsieJavaScriptEngine/ActiveScript/ClassicActiveScriptJsEngine.cs index 245cdd1..d9309fe 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/ClassicActiveScriptJsEngine.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/ClassicActiveScriptJsEngine.cs @@ -1,45 +1,141 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - using Constants; +#if NETFRAMEWORK +using System.Collections.Generic; + +using MsieJavaScriptEngine.Constants; + +using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; +namespace MsieJavaScriptEngine.ActiveScript +{ /// - /// Classic MSIE JavaScript engine + /// Active Script version of Classic JS engine /// - internal sealed class ClassicActiveScriptJsEngine : ActiveScriptJsEngineBase + internal sealed partial class ClassicActiveScriptJsEngine : ActiveScriptJsEngineBase { - private const string CLASSIC_CLSID = "{f414c260-6ac0-11cf-b6d1-00aa00bbbb58}"; - /// - /// Flag indicating whether this JavaScript engine is supported + /// Flag indicating whether this JS engine is supported /// private static bool? _isSupported; /// /// Support synchronizer /// - private static object _supportSynchronizer = new object(); + private static Lock _supportSynchronizer = new Lock(); + + /// + /// Mapping of error numbers and types + /// + private static readonly Dictionary _runtimeErrorTypeMap = new Dictionary + { + { JScriptRuntimeErrorNumber.OutOfStackSpace, JsErrorType.Common }, + { JScriptRuntimeErrorNumber.CannotAssignToThisKeyword, JsErrorType.Reference }, + { JScriptRuntimeErrorNumber.NumberExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.FunctionExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.CannotAssignToFunctionResult, JsErrorType.Reference }, + { JScriptRuntimeErrorNumber.StringExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.DateObjectExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.ObjectExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.IllegalAssignment, JsErrorType.Reference }, + { JScriptRuntimeErrorNumber.UndefinedIdentifier, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.BooleanExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.ObjectMemberExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.VbArrayExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.JavaScriptObjectExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.EnumeratorObjectExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.RegularExpressionObjectExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.SyntaxErrorInRegularExpression, JsErrorType.RegExp }, + { JScriptRuntimeErrorNumber.UnexpectedQuantifier, JsErrorType.RegExp }, + { JScriptRuntimeErrorNumber.ExpectedRightSquareBracketInRegularExpression, JsErrorType.RegExp }, + { JScriptRuntimeErrorNumber.ExpectedRightParenthesisInRegularExpression, JsErrorType.RegExp }, + { JScriptRuntimeErrorNumber.InvalidRangeInCharacterSet, JsErrorType.RegExp }, + { JScriptRuntimeErrorNumber.ExceptionThrownAndNotCaught, string.Empty }, + { JScriptRuntimeErrorNumber.FunctionDoesNotHaveValidPrototypeObject, string.Empty }, + { JScriptRuntimeErrorNumber.UriToBeEncodedContainsInvalidCharacter, JsErrorType.URI }, + { JScriptRuntimeErrorNumber.UriToBeDecodedIsNotValidEncoding, JsErrorType.URI }, + { JScriptRuntimeErrorNumber.NumberOfFractionalDigitsIsOutOfRange, JsErrorType.Range }, + { JScriptRuntimeErrorNumber.PrecisionOutOfRange, JsErrorType.Range }, + { JScriptRuntimeErrorNumber.ArrayOrArgumentsObjectExpected, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.ArrayLengthMustBeFinitePositiveInteger, JsErrorType.Range }, + { JScriptRuntimeErrorNumber.ArrayLengthMustBeAssignedFinitePositiveNumber, JsErrorType.Range }, + { JScriptRuntimeErrorNumber.CircularReferenceInValueArgumentNotSupported, JsErrorType.Type }, + { JScriptRuntimeErrorNumber.InvalidReplacerArgument, string.Empty } + }; /// - /// Constructs instance of the Classic MSIE JavaScript engine + /// Constructs an instance of the Classic Active Script engine /// - /// Flag for whether to use the ECMAScript 5 Polyfill - /// Flag for whether to use the JSON2 library - public ClassicActiveScriptJsEngine(bool useEcmaScript5Polyfill, bool useJson2Library) - : base(CLASSIC_CLSID, JsEngineMode.Classic, "6", - ScriptLanguageVersion.None, useEcmaScript5Polyfill, useJson2Library) + /// JS engine settings + public ClassicActiveScriptJsEngine(JsEngineSettings settings) + : base(settings, ClassId.Classic, ScriptLanguageVersion.None, "6", "Microsoft JScript ") { } /// - /// Checks a support of the Classic MSIE JavaScript engine on the machine + /// Checks a support of the Classic Active Script engine on the machine /// - /// Result of check (true - supports; false - does not support) + /// Result of check (true - supports; false - does not support) public static bool IsSupported() { - bool isSupported = IsSupported(CLASSIC_CLSID, ref _isSupported, ref _supportSynchronizer); + bool isSupported = IsSupported(ClassId.Classic, ref _isSupported, ref _supportSynchronizer); return isSupported; } + + /// + /// Gets a error type by number + /// + /// Error number + /// Error type + protected override string GetErrorTypeByNumber(int errorNumber) + { + return ActiveScriptJsErrorHelpers.GetErrorTypeByNumber(errorNumber, _runtimeErrorTypeMap); + } + + #region ActiveScriptJsEngineBase overrides + + /// + /// Creates a instance of the Active Script site + /// + /// Instance of the Active Script site + protected override ScriptSiteBase CreateScriptSite() + { + return new ScriptSite(this); + } + + protected override void InnerRemoveVariable(string variableName) + { + if (_hostItems.ContainsKey(variableName)) + { + _hostItems.Remove(variableName); + } + else + { + InnerSetVariableValue(variableName, null); + } + } + + #region IInnerJsEngine implementation + + public override bool SupportsScriptPrecompilation + { + get { return false; } + } + + + public override void Interrupt() + { + var exceptionInfo = new EXCEPINFO + { + scode = ComErrorCode.E_ABORT + }; + _activeScriptWrapper.InterruptScriptThread(ScriptThreadId.Base, ref exceptionInfo, + ScriptInterruptFlags.None); + } + + #endregion + + #endregion } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/AppBreakFlags.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/AppBreakFlags.cs new file mode 100644 index 0000000..f18226d --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/AppBreakFlags.cs @@ -0,0 +1,62 @@ +#if NETFRAMEWORK +using System; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Indicate the current debug state for applications and threads + /// + [Flags] + internal enum AppBreakFlags : uint + { + None = 0, + + /// + /// Language engine should break immediately on all threads with + /// + /// + DebuggerBlock = 0x00000001, + + /// + /// Language engine should break immediately with + /// + DebuggerHalt = 0x00000002, + + /// + /// Language engine should break immediately in the stepping thread with + /// + /// + Step = 0x00010000, + + /// + /// The application is in nested execution on a breakpoint + /// + Nested = 0x00020000, + + /// + /// The debugger is stepping at the source level + /// + StepTypeSource = 0x00000000, + + /// + /// The debugger is stepping at the byte code level + /// + StepTypeByteCode = 0x00100000, + + /// + /// The debugger is stepping at the machine level + /// + StepTypeMachine = 0x00200000, + + /// + /// Mask for factoring out the step types + /// + StepTypeMask = 0x00F00000, + + /// + /// A breakpoint is in progress + /// + InBreakpoint = 0x80000000 + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/BreakReason.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/BreakReason.cs new file mode 100644 index 0000000..e632c22 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/BreakReason.cs @@ -0,0 +1,50 @@ +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Indicates what caused the break + /// + internal enum BreakReason + { + /// + /// The language engine is in the stepping mode + /// + Step, + + /// + /// The language engine encountered an explicit breakpoint + /// + Breakpoint, + + /// + /// The language engine encountered a debugger block on another thread + /// + DebuggerBlock, + + /// + /// The host requested a break + /// + HostInitiated, + + /// + /// The language engine requested a break + /// + LanguageInitiated, + + /// + /// The debugger IDE requested a break + /// + DebuggerHalt, + + /// + /// An execution error caused the break + /// + Error, + + /// + /// Caused by JIT Debugging startup + /// + Jit + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/BreakResumeAction.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/BreakResumeAction.cs new file mode 100644 index 0000000..67ba243 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/BreakResumeAction.cs @@ -0,0 +1,45 @@ +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Describes ways to continue from a breakpoint + /// + internal enum BreakResumeAction + { + /// + /// Aborts the application + /// + Abort, + + /// + /// Continues running + /// + Continue, + + /// + /// Steps into a procedure + /// + StepInto, + + /// + /// Steps over a procedure + /// + StepOver, + + /// + /// Steps out of the current procedure + /// + StepOut, + + /// + /// Continues running with state + /// + Ignore, + + /// + /// Steps to the next document + /// + StepDocument + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/BreakpointState.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/BreakpointState.cs new file mode 100644 index 0000000..71824dd --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/BreakpointState.cs @@ -0,0 +1,25 @@ +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Indicates the state of a breakpoint + /// + internal enum BreakpointState + { + /// + /// The breakpoint no longer exists, but there are still references to it + /// + Deleted, + + /// + /// The breakpoint exists but is disabled + /// + Disabled, + + /// + /// The breakpoint exists and is enabled + /// + Enabled + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/DebugApplicationWrapper.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/DebugApplicationWrapper.cs new file mode 100644 index 0000000..e58dd18 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/DebugApplicationWrapper.cs @@ -0,0 +1,143 @@ +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Wrapper for debug application + /// + internal sealed class DebugApplicationWrapper + { + /// + /// Flag that the current debug application is a 64-bit + /// + private readonly bool _is64Bit; + + /// + /// Instance of 32-bit debug application + /// + private readonly IDebugApplication32 _debugApplication32; + + /// + /// Instance of 64-bit debug application + /// + private readonly IDebugApplication64 _debugApplication64; + + /// + /// Gets a instance of 32-bit debug application + /// + public IDebugApplication32 DebugApplication32 + { + get { return _debugApplication32; } + } + + /// + /// Gets a instance of 64-bit debug application + /// + public IDebugApplication64 DebugApplication64 + { + get { return _debugApplication64; } + } + + + /// + /// Constructs an instance of the wrapper for debug application + /// + /// Instance of 32-bit debug application + public DebugApplicationWrapper(IDebugApplication32 debugApplication) + { + _is64Bit = false; + _debugApplication32 = debugApplication; + } + + /// + /// Constructs an instance of the wrapper for debug application + /// + /// Instance of 64-bit debug application + public DebugApplicationWrapper(IDebugApplication64 debugApplication) + { + _is64Bit = true; + _debugApplication64 = debugApplication; + } + + + /// + /// Sets the name of the application + /// + /// The name of the application + public void SetName(string name) + { + if (_is64Bit) + { + _debugApplication64.SetName(name); + } + else + { + _debugApplication32.SetName(name); + } + } + + /// + /// Returns the application node under which all nodes associated with the application are added + /// + /// The debug application node under which all nodes associated with + /// the application are added + public void GetRootNode(out IDebugApplicationNode node) + { + if (_is64Bit) + { + _debugApplication64.GetRootNode(out node); + } + else + { + _debugApplication32.GetRootNode(out node); + } + } + + /// + /// Creates a new application node that is associated with a specific document provider + /// + /// The application node associated with this document provider + public void CreateApplicationNode(out IDebugApplicationNode node) + { + if (_is64Bit) + { + _debugApplication64.CreateApplicationNode(out node); + } + else + { + _debugApplication32.CreateApplicationNode(out node); + } + } + + /// + /// Returns the current debugger connected to the application + /// + /// The current debugger connected to the application + /// The method returns an HRESULT + public uint GetDebugger(out IApplicationDebugger debugger) + { + uint result = _is64Bit ? + _debugApplication64.GetDebugger(out debugger) + : + _debugApplication32.GetDebugger(out debugger) + ; + + return result; + } + + /// + /// Causes this application to release all references and enter an inactive state + /// + public void Close() + { + if (_is64Bit) + { + _debugApplication64.Close(); + } + else + { + _debugApplication32.Close(); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/DebugDocument.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/DebugDocument.cs new file mode 100644 index 0000000..7c7137d --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/DebugDocument.cs @@ -0,0 +1,253 @@ +#if NETFRAMEWORK +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.InteropServices; + +using MsieJavaScriptEngine.Helpers; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Debug document + /// + internal sealed class DebugDocument : IDebugDocumentInfo, IDebugDocumentProvider, IDebugDocument, + IDebugDocumentText + { + /// + /// Active Script wrapper + /// + private readonly IActiveScriptWrapper _activeScriptWrapper; + + /// + /// Wrapper for debug application + /// + private readonly DebugApplicationWrapper _debugApplicationWrapper; + + /// + /// Source context + /// + private readonly UIntPtr _sourceContext; + + /// + /// Document name + /// + private readonly string _name; + + /// + /// Script text + /// + private readonly string _code; + + /// + /// List of source code lines + /// + private readonly List _lines = new List(); + + /// + /// Debug application node + /// + private IDebugApplicationNode _node; + + /// + /// Gets a script text + /// + public string Code + { + get { return _code; } + } + + + /// + /// Constructs an instance of the debug document + /// + /// Active Script wrapper + /// Wrapper for debug application + /// Source context + /// Document name + /// Script text + public DebugDocument(IActiveScriptWrapper activeScriptWrapper, + DebugApplicationWrapper debugApplicationWrapper, UIntPtr sourceContext, string name, + string code) + { + _activeScriptWrapper = activeScriptWrapper; + _debugApplicationWrapper = debugApplicationWrapper; + _sourceContext = sourceContext; + _name = name; + _code = code; + + Initialize(); + } + + + /// + /// Initializes a debug document + /// + private void Initialize() + { + int documentStartPosition = 0; + int documentEndPosition = _code.Length - 1; + int lineBreakPosition = int.MinValue; + int lineBreakLength = 0; + uint lineNumber = 1; + + do + { + int linePosition = lineBreakPosition == int.MinValue ? + documentStartPosition : lineBreakPosition + lineBreakLength; + int remainderLength = documentEndPosition - linePosition + 1; + + TextHelpers.FindNextLineBreak(_code, linePosition, remainderLength, + out lineBreakPosition, out lineBreakLength); + + int lineLength = lineBreakPosition != -1 ? lineBreakPosition - linePosition : 0; + + _lines.Add(new DebugLineInfo(lineNumber, (uint)linePosition, (uint)lineLength, (uint)lineBreakLength)); + lineNumber++; + } + while (lineBreakPosition != -1 && lineBreakPosition <= documentEndPosition); + + _debugApplicationWrapper.CreateApplicationNode(out _node); + _node.SetDocumentProvider(this); + + IDebugApplicationNode rootNode; + _debugApplicationWrapper.GetRootNode(out rootNode); + _node.Attach(rootNode); + } + + /// + /// Closes a debug document + /// + public void Close() + { + if (_node is not null) + { + _node.Detach(); + _node.Close(); + _node = null; + } + + if (_lines is not null) + { + _lines.Clear(); + } + } + + #region IDebugDocumentInfo implementation + + public void GetName(DocumentNameType type, out string documentName) + { + documentName = _name; + } + + public void GetDocumentClassId(out Guid clsid) + { + clsid = Guid.Empty; + } + + #endregion + + #region IDebugDocumentProvider implementation + + public void GetDocument(out IDebugDocument document) + { + document = this; + } + + #endregion + + #region IDebugDocumentText implementation + + public void GetDocumentAttributes(out TextDocAttrs attrs) + { + attrs = TextDocAttrs.ReadOnly; + } + + public void GetSize(out uint numLines, out uint length) + { + numLines = (uint)_lines.Count; + length = (uint)_code.Length; + } + + public void GetPositionOfLine(uint lineNumber, out uint position) + { + position = 0; + int lineCount = _lines.Count; + + if (lineNumber == 0 || lineNumber > lineCount) + { + throw new ArgumentOutOfRangeException("lineNumber"); + } + + if (lineCount > 0) + { + int lineIndex = (int)lineNumber - 1; + position = _lines[lineIndex].Position; + } + } + + public void GetLineOfPosition(uint position, out uint lineNumber, out uint offsetInLine) + { + if (position >= _code.Length) + { + throw new ArgumentOutOfRangeException("position"); + } + + lineNumber = 0; + offsetInLine = position; + int lineCount = _lines.Count; + + for (int lineIndex = 0; lineIndex < lineCount; lineIndex++) + { + DebugLineInfo line = _lines[lineIndex]; + lineNumber = line.Number; + uint fullLineLength = line.Length + line.BreakLength; + + if (offsetInLine < fullLineLength) + { + break; + } + + offsetInLine -= fullLineLength; + } + } + + public void GetText(uint position, IntPtr pChars, IntPtr pAttrs, ref uint length, uint maxChars) + { + var codeLength = (uint)_code.Length; + if (position < codeLength) + { + length = Math.Min(codeLength - position, maxChars); + + if (pChars != IntPtr.Zero) + { + Marshal.Copy(_code.ToCharArray((int)position, (int)length), 0, pChars, (int)length); + } + + if (pAttrs != IntPtr.Zero) + { + short[] attrs = Enumerable.Repeat((short)SourceTextAttrs.None, (int)length).ToArray(); + Marshal.Copy(attrs, 0, pAttrs, (int)length); + } + } + } + + public void GetPositionOfContext(IDebugDocumentContext context, out uint position, out uint length) + { + var documentContext = (DebugDocumentContext)context; + position = documentContext.Position; + length = documentContext.Length; + } + + public void GetContextOfPosition(uint position, uint length, out IDebugDocumentContext context) + { + IEnumDebugCodeContexts enumCodeContexts; + _activeScriptWrapper.EnumCodeContextsOfPosition(_sourceContext, position, length, + out enumCodeContexts); + context = new DebugDocumentContext(this, position, length, enumCodeContexts); + } + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/DebugDocumentContext.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/DebugDocumentContext.cs new file mode 100644 index 0000000..832ced4 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/DebugDocumentContext.cs @@ -0,0 +1,78 @@ +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Debug document context + /// + internal sealed class DebugDocumentContext : IDebugDocumentContext + { + /// + /// Debug document + /// + private readonly DebugDocument _document; + + /// + /// Position + /// + private readonly uint _position; + + /// + /// Length + /// + private readonly uint _length; + + /// + /// Code context enumerator + /// + private readonly IEnumDebugCodeContexts _enumCodeContexts; + + /// + /// Gets a position + /// + public uint Position + { + get { return _position; } + } + + /// + /// Gets a length + /// + public uint Length + { + get { return _length; } + } + + + /// + /// Constructs an instance of the debug document context + /// + /// Debug document + /// Position + /// Length + /// Code context enumerator + public DebugDocumentContext(DebugDocument document, uint position, uint length, + IEnumDebugCodeContexts enumCodeContexts) + { + _document = document; + _position = position; + _length = length; + _enumCodeContexts = enumCodeContexts; + } + + + #region IDebugDocumentContext implementation + + public void GetDocument(out IDebugDocument debugDocument) + { + debugDocument = _document; + } + + public void EnumCodeContexts(out IEnumDebugCodeContexts enumContexts) + { + _enumCodeContexts.Clone(out enumContexts); + } + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/DebugLineInfo.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/DebugLineInfo.cs new file mode 100644 index 0000000..7300bde --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/DebugLineInfo.cs @@ -0,0 +1,46 @@ +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Information about line of source code + /// + internal struct DebugLineInfo + { + /// + /// Gets a line number + /// + public readonly uint Number; + + /// + /// Gets a position of line + /// + public readonly uint Position; + + /// + /// Gets a length of line + /// + public readonly uint Length; + + /// + /// Gets a length of line break + /// + public readonly uint BreakLength; + + + /// + /// Constructs an instance of the information about line of source code + /// + /// Line number + /// Position of line + /// Length of line + /// Length of line break + public DebugLineInfo(uint number, uint position, uint length, uint breakLength) + { + Number = number; + Position = position; + Length = length; + BreakLength = breakLength; + } + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/DebugStackFrameDescriptor.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/DebugStackFrameDescriptor.cs new file mode 100644 index 0000000..4823b28 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/DebugStackFrameDescriptor.cs @@ -0,0 +1,49 @@ +#if NETFRAMEWORK +using System; +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Enumerates stack frames and merges output from several enumerators on the same thread + /// + /// + /// The process debug manager uses this structure to sort the stack frames from + /// multiple script engines. By convention, stacks grow down. Consequently, on architectures + /// where stacks grow up, the addresses should be twos-complemented. + /// + [StructLayout(LayoutKind.Sequential)] + internal struct DebugStackFrameDescriptor + { + /// + /// The stack frame object + /// + [MarshalAs(UnmanagedType.Interface)] + public IDebugStackFrame Frame; + + /// + /// A machine-dependent representation of the lower range of physical addresses + /// associated with this stack frame + /// + public uint Minimum; + + /// + /// A machine-dependent representation of the upper range of physical addresses + /// associated with this stack frame + /// + public uint Limit; + + /// + /// Flag that indicates that the frame is being processed + /// + [MarshalAs(UnmanagedType.Bool)] + public bool IsFinal; + + /// + /// If this parameter is not null, the current enumerator merging should stop and + /// a new one should be started. The object indicates how to start the new enumeration. + /// + public IntPtr pFinalObject; + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/DocumentNameType.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/DocumentNameType.cs new file mode 100644 index 0000000..9353c03 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/DocumentNameType.cs @@ -0,0 +1,35 @@ +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Describes which type to get for a document + /// + internal enum DocumentNameType + { + /// + /// Gets the name as it appears in the application tree + /// + AppNode, + + /// + /// Gets the name as it appears on the viewer title bar + /// + Title, + + /// + /// Gets the file name without a path + /// + FileTail, + + /// + /// Gets the URL of the document + /// + Url, + + /// + /// Gets the title appended with enumeration for identification + /// + UniqueTitle + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/ErrorResumeAction.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/ErrorResumeAction.cs new file mode 100644 index 0000000..c294997 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/ErrorResumeAction.cs @@ -0,0 +1,25 @@ +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Describes how to continue from a runtime error + /// + internal enum ErrorResumeAction + { + /// + /// Re-executes the statement that produced the error + /// + ReexecuteErrorStatement, + + /// + /// Lets the language engine handle the error + /// + AbortCallAndReturnErrorToCaller, + + /// + /// Resumes execution in the code following the statement that produced the error + /// + SkipErrorStatement + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptDebug32.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptDebug32.cs new file mode 100644 index 0000000..21c2a6e --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptDebug32.cs @@ -0,0 +1,66 @@ +#if NETFRAMEWORK +using System; +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Implemented by script engines that support debugging + /// + [ComImport] + [Guid("51973c10-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IActiveScriptDebug32 + { + /// + /// Returns the text attributes for an arbitrary block of script text + /// + /// The script block text + /// The number of characters in the script block text + /// End of script block delimiter + /// Flags associated with the script block + /// Buffer to contain the returned attributes + void GetScriptTextAttributes( + [In] [MarshalAs(UnmanagedType.LPWStr)] string code, + [In] uint length, + [In] [MarshalAs(UnmanagedType.LPWStr)] string delimiter, + [In] ScriptTextFlags flags, + [In] [Out] ref IntPtr pAttrs + ); + + /// + /// Returns the text attributes for an arbitrary scriptlet + /// + /// The scriptlet text + /// The number of characters in the scriptlet text + /// End of scriptlet delimiter + /// Flags associated with the scriptlet + /// Buffer to contain the returned attributes + void GetScriptletTextAttributes( + [In] [MarshalAs(UnmanagedType.LPWStr)] string code, + [In] uint length, + [In] [MarshalAs(UnmanagedType.LPWStr)] string delimiter, + [In] ScriptTextFlags flags, + [In] [Out] ref IntPtr pAttrs + ); + + /// + /// Used by a smart host to delegate the method + /// + /// The source context as provided to + /// or + /// + /// Character offset relative to start of script text + /// Number of characters in this context + /// An enumerator of the code contexts in the specified range + /// The method returns an HRESULT + [PreserveSig] + uint EnumCodeContextsOfPosition( + [In] uint sourceContext, + [In] uint offset, + [In] uint length, + [Out] [MarshalAs(UnmanagedType.Interface)] out IEnumDebugCodeContexts enumContexts + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptDebug64.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptDebug64.cs new file mode 100644 index 0000000..0a23f42 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptDebug64.cs @@ -0,0 +1,66 @@ +#if NETFRAMEWORK +using System; +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Implemented by script engines that support debugging + /// + [ComImport] + [Guid("bc437e23-f5b8-47f4-bb79-7d1ce5483b86")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IActiveScriptDebug64 + { + /// + /// Returns the text attributes for an arbitrary block of script text + /// + /// The script block text + /// The number of characters in the script block text + /// End of script block delimiter + /// Flags associated with the script block + /// Buffer to contain the returned attributes + void GetScriptTextAttributes( + [In] [MarshalAs(UnmanagedType.LPWStr)] string code, + [In] uint length, + [In] [MarshalAs(UnmanagedType.LPWStr)] string delimiter, + [In] ScriptTextFlags flags, + [In] [Out] ref IntPtr pAttrs + ); + + /// + /// Returns the text attributes for an arbitrary scriptlet + /// + /// The scriptlet text + /// The number of characters in the scriptlet text + /// End of scriptlet delimiter + /// Flags associated with the scriptlet + /// Buffer to contain the returned attributes + void GetScriptletTextAttributes( + [In] [MarshalAs(UnmanagedType.LPWStr)] string code, + [In] uint length, + [In] [MarshalAs(UnmanagedType.LPWStr)] string delimiter, + [In] ScriptTextFlags flags, + [In] [Out] ref IntPtr pAttrs + ); + + /// + /// Used by a smart host to delegate the method + /// + /// The source context as provided to + /// or + /// + /// Character offset relative to start of script text + /// Number of characters in this context + /// An enumerator of the code contexts in the specified range + /// The method returns an HRESULT + [PreserveSig] + uint EnumCodeContextsOfPosition( + [In] ulong sourceContext, + [In] uint offset, + [In] uint length, + [Out] [MarshalAs(UnmanagedType.Interface)] out IEnumDebugCodeContexts enumContexts + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptErrorDebug.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptErrorDebug.cs new file mode 100644 index 0000000..6932036 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptErrorDebug.cs @@ -0,0 +1,52 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Provides document context information for compile-time errors and run-time exceptions + /// + [ComImport] + [Guid("51973c12-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IActiveScriptErrorDebug // : IActiveScriptError + { + #region IActiveScriptError methods + + /// + /// Retrieves information about an error that occurred while the scripting engine was running a script + /// + /// An EXCEPINFO structure that receives error information + void GetExceptionInfo( + [Out] out EXCEPINFO exceptionInfo + ); + + /// + /// Retrieves the location in the source code where an error occurred while the scripting engine + /// was running a script + /// + /// A cookie that identifies the context. The interpretation of + /// this parameter depends on the host application. + /// The line number in the source file where the error occurred + /// The character position in the line where the error occurred + void GetSourcePosition( + [Out] out uint sourceContext, + [Out] out uint lineNumber, + [Out] out int characterPosition + ); + + /// + /// Retrieves the line in the source file where an error occurred while a scripting engine + /// was running a script + /// + /// The line of source code in which the error occurred + void GetSourceLineText( + [Out] [MarshalAs(UnmanagedType.BStr)] out string sourceLine + ); + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptSiteDebug32.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptSiteDebug32.cs new file mode 100644 index 0000000..a7c0618 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptSiteDebug32.cs @@ -0,0 +1,64 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Smart hosts implement the interface to perform document management + /// and to participate in debugging + /// + [ComImport] + [Guid("51973c11-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IActiveScriptSiteDebug32 + { + /// + /// Used by the language engine to delegate + /// + /// The source context as provided to + /// or + /// + /// Character offset relative to start of script block or scriptlet + /// Number of characters in this context + /// The document context corresponding to this character-position + /// range + void GetDocumentContextFromPosition( + [In] uint sourceContext, + [In] uint offset, + [In] uint length, + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugDocumentContext documentContext + ); + + /// + /// Returns the debug application object associated with this script site + /// + /// The debug application object associated with the script site + void GetApplication( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugApplication32 application + ); + + /// + /// Gets the application node under which script documents should be added + /// + /// The debug application node that holds script documents + void GetRootApplicationNode( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugApplicationNode node + ); + + /// + /// Allows a smart host to determine how to handle run-time errors + /// + /// The run-time error that occurred + /// Flag indicating whether to pass the error to the debugger to + /// do JIT debugging + /// Flag indicating whether to call + /// when the user decides to continue without + /// debugging + void OnScriptErrorDebug( + [In] [MarshalAs(UnmanagedType.Interface)] IActiveScriptErrorDebug errorDebug, + [Out] [MarshalAs(UnmanagedType.Bool)] out bool enterDebugger, + [Out] [MarshalAs(UnmanagedType.Bool)] out bool callOnScriptErrorWhenContinuing + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptSiteDebug64.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptSiteDebug64.cs new file mode 100644 index 0000000..0cec490 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptSiteDebug64.cs @@ -0,0 +1,64 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Smart hosts implement the interface to perform document management + /// and to participate in debugging + /// + [ComImport] + [Guid("d6b96b0a-7463-402c-92ac-89984226942f")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IActiveScriptSiteDebug64 + { + /// + /// Used by the language engine to delegate IDebugCodeContext.GetSourceContext + /// + /// The source context as provided to + /// or + /// + /// Character offset relative to start of script block or scriptlet + /// Number of characters in this context + /// The document context corresponding to this character-position + /// range + void GetDocumentContextFromPosition( + [In] ulong sourceContext, + [In] uint offset, + [In] uint length, + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugDocumentContext documentContext + ); + + /// + /// Returns the debug application object associated with this script site + /// + /// The debug application object associated with the script site + void GetApplication( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugApplication64 application + ); + + /// + /// Gets the application node under which script documents should be added + /// + /// The debug application node that holds script documents + void GetRootApplicationNode( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugApplicationNode node + ); + + /// + /// Allows a smart host to determine how to handle run-time errors + /// + /// The run-time error that occurred + /// Flag indicating whether to pass the error to the debugger to + /// do JIT debugging + /// Flag indicating whether to call + /// when the user decides to continue without + /// debugging + void OnScriptErrorDebug( + [In] [MarshalAs(UnmanagedType.Interface)] IActiveScriptErrorDebug errorDebug, + [Out] [MarshalAs(UnmanagedType.Bool)] out bool enterDebugger, + [Out] [MarshalAs(UnmanagedType.Bool)] out bool callOnScriptErrorWhenContinuing + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptSiteDebugEx.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptSiteDebugEx.cs new file mode 100644 index 0000000..b44c9fd --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IActiveScriptSiteDebugEx.cs @@ -0,0 +1,35 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Implement this interface along with the + /// or interface if you are writing a host that needs + /// to get a notification of a run-time error in an application and optionally attach to + /// the application for debugging. The Process Debug Manager provides notification through + /// or if a Just-In-Time + /// script debugger is found on the computer. If no Just-In-Time script debugger is found, + /// the PDM provides notification through instead. + /// + [ComImport] + [Guid("bb722ccb-6ad2-41c6-b780-af9c03ee69f5")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IActiveScriptSiteDebugEx + { + /// + /// Informs the host about a script run-time error when the Process Debug Manager does not + /// find a Just In Time script debugger + /// + /// The run-time error that occurred + /// Whether to call + /// or + /// if the user decides to + /// continue without debugging + void OnCanNotJitScriptErrorDebug( + [In] [MarshalAs(UnmanagedType.Interface)] IActiveScriptErrorDebug errorDebug, + [Out] [MarshalAs(UnmanagedType.Bool)] out bool callOnScriptErrorWhenContinuing + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IApplicationDebugger.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IApplicationDebugger.cs new file mode 100644 index 0000000..e72e43d --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IApplicationDebugger.cs @@ -0,0 +1,15 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// The primary interface exposed by a debugger + /// + [ComImport] + [Guid("51973c2a-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IApplicationDebugger + { } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugApplication32.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugApplication32.cs new file mode 100644 index 0000000..87a78c3 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugApplication32.cs @@ -0,0 +1,303 @@ +using System; +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Exposes non-remote debugging methods for use by language engines and hosts + /// + [ComImport] + [Guid("51973C32-CB0C-11d0-B5C9-00A0244A0E7A")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugApplication32 // : IRemoteDebugApplication + { +#if NETFRAMEWORK + #region IRemoteDebugApplication methods + + /// + /// Continues an application that is currently in a breakpoint + /// + /// For stepping modes, the thread which is to be affected by the stepping mode + /// The action to take upon resuming the application + /// The action to take in the case that the application stopped because of an error + void ResumeFromBreakPoint( + [In] [MarshalAs(UnmanagedType.Interface)] IRemoteDebugApplicationThread thread, + [In] BreakResumeAction breakResumeAction, + [In] ErrorResumeAction errorResumeAction + ); + + /// + /// Causes the application to break into the debugger at the earliest opportunity + /// + void CauseBreak(); + + /// + /// Connects a debugger to this application + /// + /// The debugger to attach to this application + void ConnectDebugger( + [In] [MarshalAs(UnmanagedType.Interface)] IApplicationDebugger debugger + ); + + /// + /// Disconnects the current debugger from the application + /// + void DisconnectDebugger(); + + /// + /// Returns the current debugger connected to the application + /// + /// The current debugger connected to the application + /// The method returns an HRESULT + [PreserveSig] + uint GetDebugger( + [Out] [MarshalAs(UnmanagedType.Interface)] out IApplicationDebugger debugger + ); + + /// + /// Allows the creation of objects in the application process by code that is out-of-process + /// to the application + /// + /// Class identifier (CLSID) of the object to create + /// The aggregate object's IUnknown interface + /// Context for running executable code + /// The interface identifier used to communicate with the object + /// Variable that receives the interface pointer requested in + void CreateInstanceAtApplication( + [In] ref Guid clsid, + [In] [MarshalAs(UnmanagedType.IUnknown)] object outer, + [In] uint clsContext, + [In] ref Guid iid, + [MarshalAs(UnmanagedType.IUnknown, IidParameterIndex = 3)] out object instance + ); + + /// + /// Indicates if the application is responsive + /// + void QueryAlive(); + + /// + /// Enumerates all threads known to be associated with the application + /// + /// Enumerator that lists all threads known to be associated with + /// the application + void EnumThreads( + [Out] [MarshalAs(UnmanagedType.Interface)] out IEnumRemoteDebugApplicationThreads enumThreads + ); + + /// + /// Returns the name of this application node + /// + /// Name of this application node + void GetName( + [Out] [MarshalAs(UnmanagedType.BStr)] out string name + ); + + /// + /// Returns the application node under which all nodes associated with the application are added + /// + /// The debug application node under which all nodes associated with + /// the application are added + void GetRootNode( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugApplicationNode node + ); + + /// + /// Enumerates the global expression contexts for all languages running in this application + /// + /// Enumerator that lists the global expression contexts for all + /// languages running in this application + void EnumGlobalExpressionContexts( + [Out] [MarshalAs(UnmanagedType.Interface)] out IEnumDebugExpressionContexts enumContexts + ); + + #endregion + + /// + /// Sets the name of the application + /// + /// The name of the application + void SetName( + [In] [MarshalAs(UnmanagedType.LPWStr)] string name + ); + + /// + /// Notifies the process debug manager that a language engine in single-step mode is about + /// to return to its caller + /// + void StepOutComplete(); + + /// + /// Causes the given string to be displayed by the debugger IDE + /// + /// String to display in the debugger + void DebugOutput( + [In] [MarshalAs(UnmanagedType.LPWStr)] string str + ); + + /// + /// Starts the default debugger IDE and attaches a debug session to this application, if one + /// is not already attached + /// + void StartDebugSession(); + + /// + /// Causes the current thread to block and sends a notification of the breakpoint to the debugger IDE + /// + /// The reason for the break + /// Action to take when the debugger resumes the application + void HandleBreakPoint( + [In] BreakReason reason, + [Out] out BreakResumeAction resumeAction + ); + + /// + /// Causes this application to release all references and enter an inactive state + /// + void Close(); + + /// + /// Returns the current break flags for the application + /// + /// The current break flags for the application + /// The currently running thread + void GetBreakFlags( + [Out] out AppBreakFlags flags, + [Out] [MarshalAs(UnmanagedType.Interface)] out IRemoteDebugApplicationThread thread + ); + + /// + /// Returns the thread associated with the currently running thread + /// + /// The thread associated with the currently running thread + void GetCurrentThread( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugApplicationThread thread + ); + + /// + /// Provides asynchronous access to a given synchronous debug operation + /// + /// The synchronous debug operation object + /// The asynchronous debug operation object + void CreateAsyncDebugOperation( + [In] [MarshalAs(UnmanagedType.Interface)] IDebugSyncOperation syncOperation, + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugAsyncOperation asyncOperation + ); + + /// + /// Adds a stack frame enumerator provider to this application + /// + /// The stack frame enumerator provider to add to this application + /// A cookie that is used to remove this stack frame enumerator provider + /// from the application + void AddStackFrameSniffer( + [In] [MarshalAs(UnmanagedType.Interface)] IDebugStackFrameSniffer sniffer, + [Out] out uint cookie + ); + + /// + /// Removes a stack frame enumerator provider from this application + /// + /// The cookie returned by the method + /// when the stack frame enumerator provider was added + void RemoveStackFrameSniffer( + [In] uint cookie + ); + + /// + /// Determines if the current running thread is the debugger thread + /// + /// The method returns an HRESULT + [PreserveSig] + uint QueryCurrentThreadIsDebuggerThread(); + + /// + /// Provides a mechanism for the caller to run code in the debugger thread + /// + /// The object to call + /// First parameter to pass to the IDebugThreadCall.ThreadCallHandler method + /// Second parameter to pass to the IDebugThreadCall.ThreadCallHandler method + /// Third parameter to pass to the IDebugThreadCall.ThreadCallHandler method + void SynchronousCallInDebuggerThread( + [In] [MarshalAs(UnmanagedType.Interface)] IDebugThreadCall32 call, + [In] uint param1, + [In] uint param2, + [In] uint param3 + ); + + /// + /// Creates a new application node that is associated with a specific document provider + /// + /// The application node associated with this document provider + void CreateApplicationNode( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugApplicationNode node + ); + + /// + /// Fires a generic event to the debugger's interface + /// + /// A GUID for the object + /// An event object to pass to the debugger + void FireDebuggerEvent( + [In] ref Guid iid, + [In] [MarshalAs(UnmanagedType.IUnknown, IidParameterIndex = 0)] object eventObject + ); + + /// + /// Causes the current thread to block and sends a notification of the error to the debugger IDE + /// + /// The error that occurred + /// The script site of the thread + /// Action to take when the debugger resumes the application + /// Action to take when the debugger resumes the application + /// if there is an error + /// Flag which is true if the engine should call + /// the method + void HandleRuntimeError( + [In] [MarshalAs(UnmanagedType.Interface)] IActiveScriptErrorDebug errorDebug, + [In] [MarshalAs(UnmanagedType.Interface)] IActiveScriptSite scriptSite, + [Out] out BreakResumeAction breakResumeAction, + [Out] out ErrorResumeAction errorResumeAction, + [Out] [MarshalAs(UnmanagedType.Bool)] out bool callOnScriptError + ); + + /// + /// Determines if a JIT debugger is registered + /// + /// If the method succeeds and a JIT debugger is registered, the method returns true. + /// Otherwise, it returns false. + [PreserveSig] + [return: MarshalAs(UnmanagedType.Bool)] + bool FCanJitDebug(); + + /// + /// Determines if a JIT debugger is registered to auto-debug dumb hosts + /// + /// If the method succeeds and a JIT debugger is registered to auto-debug dumb hosts, + /// the method returns true. Otherwise, it returns false. + [PreserveSig] + [return: MarshalAs(UnmanagedType.Bool)] + bool FIsAutoJitDebugEnabled(); + + /// + /// Adds a global expression context provider to this application + /// + /// The global context provider to add to this application + /// A cookie that is used to remove this global expression context provider + /// from the application + void AddGlobalExpressionContextProvider( + [In] [MarshalAs(UnmanagedType.Interface)] IProvideExpressionContexts provider, + [Out] out uint cookie + ); + + /// + /// Removes a global expression context provider from this application + /// + /// The cookie returned by the method + /// when the global context provider was added + void RemoveGlobalExpressionContextProvider( + [In] uint cookie + ); +#endif + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugApplication64.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugApplication64.cs new file mode 100644 index 0000000..6f1993c --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugApplication64.cs @@ -0,0 +1,303 @@ +using System; +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Exposes non-remote debugging methods for use by language engines and hosts + /// + [ComImport] + [Guid("4dedc754-04c7-4f10-9e60-16a390fe6e62")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugApplication64 // : IRemoteDebugApplication + { +#if NETFRAMEWORK + #region IRemoteDebugApplication methods + + /// + /// Continues an application that is currently in a breakpoint + /// + /// For stepping modes, the thread which is to be affected by the stepping mode + /// The action to take upon resuming the application + /// The action to take in the case that the application stopped because of an error + void ResumeFromBreakPoint( + [In] [MarshalAs(UnmanagedType.Interface)] IRemoteDebugApplicationThread thread, + [In] BreakResumeAction breakResumeAction, + [In] ErrorResumeAction errorResumeAction + ); + + /// + /// Causes the application to break into the debugger at the earliest opportunity + /// + void CauseBreak(); + + /// + /// Connects a debugger to this application + /// + /// The debugger to attach to this application + void ConnectDebugger( + [In] [MarshalAs(UnmanagedType.Interface)] IApplicationDebugger debugger + ); + + /// + /// Disconnects the current debugger from the application + /// + void DisconnectDebugger(); + + /// + /// Returns the current debugger connected to the application + /// + /// The current debugger connected to the application + /// The method returns an HRESULT + [PreserveSig] + uint GetDebugger( + [Out] [MarshalAs(UnmanagedType.Interface)] out IApplicationDebugger debugger + ); + + /// + /// Allows the creation of objects in the application process by code that is out-of-process + /// to the application + /// + /// Class identifier (CLSID) of the object to create + /// The aggregate object's IUnknown interface + /// Context for running executable code + /// The interface identifier used to communicate with the object + /// Variable that receives the interface pointer requested in + void CreateInstanceAtApplication( + [In] ref Guid clsid, + [In] [MarshalAs(UnmanagedType.IUnknown)] object outer, + [In] uint clsContext, + [In] ref Guid iid, + [MarshalAs(UnmanagedType.IUnknown, IidParameterIndex = 3)] out object instance + ); + + /// + /// Indicates if the application is responsive + /// + void QueryAlive(); + + /// + /// Enumerates all threads known to be associated with the application + /// + /// Enumerator that lists all threads known to be associated with + /// the application + void EnumThreads( + [Out] [MarshalAs(UnmanagedType.Interface)] out IEnumRemoteDebugApplicationThreads enumThreads + ); + + /// + /// Returns the name of this application node + /// + /// Name of this application node + void GetName( + [Out] [MarshalAs(UnmanagedType.BStr)] out string name + ); + + /// + /// Returns the application node under which all nodes associated with the application are added + /// + /// The debug application node under which all nodes associated with + /// the application are added + void GetRootNode( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugApplicationNode node + ); + + /// + /// Enumerates the global expression contexts for all languages running in this application + /// + /// Enumerator that lists the global expression contexts for all + /// languages running in this application + void EnumGlobalExpressionContexts( + [Out] [MarshalAs(UnmanagedType.Interface)] out IEnumDebugExpressionContexts enumContexts + ); + + #endregion + + /// + /// Sets the name of the application + /// + /// The name of the application + void SetName( + [In] [MarshalAs(UnmanagedType.LPWStr)] string name + ); + + /// + /// Notifies the process debug manager that a language engine in single-step mode is about + /// to return to its caller + /// + void StepOutComplete(); + + /// + /// Causes the given string to be displayed by the debugger IDE + /// + /// String to display in the debugger + void DebugOutput( + [In] [MarshalAs(UnmanagedType.LPWStr)] string str + ); + + /// + /// Starts the default debugger IDE and attaches a debug session to this application, if one + /// is not already attached + /// + void StartDebugSession(); + + /// + /// Causes the current thread to block and sends a notification of the breakpoint to the debugger IDE + /// + /// The reason for the break + /// Action to take when the debugger resumes the application + void HandleBreakPoint( + [In] BreakReason reason, + [Out] out BreakResumeAction resumeAction + ); + + /// + /// Causes this application to release all references and enter an inactive state + /// + void Close(); + + /// + /// Returns the current break flags for the application + /// + /// The current break flags for the application + /// The currently running thread + void GetBreakFlags( + [Out] out AppBreakFlags flags, + [Out] [MarshalAs(UnmanagedType.Interface)] out IRemoteDebugApplicationThread thread + ); + + /// + /// Returns the thread associated with the currently running thread + /// + /// The thread associated with the currently running thread + void GetCurrentThread( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugApplicationThread thread + ); + + /// + /// Provides asynchronous access to a given synchronous debug operation + /// + /// The synchronous debug operation object + /// The asynchronous debug operation object + void CreateAsyncDebugOperation( + [In] [MarshalAs(UnmanagedType.Interface)] IDebugSyncOperation syncOperation, + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugAsyncOperation asyncOperation + ); + + /// + /// Adds a stack frame enumerator provider to this application + /// + /// The stack frame enumerator provider to add to this application + /// A cookie that is used to remove this stack frame enumerator provider + /// from the application + void AddStackFrameSniffer( + [In] [MarshalAs(UnmanagedType.Interface)] IDebugStackFrameSniffer sniffer, + [Out] out uint cookie + ); + + /// + /// Removes a stack frame enumerator provider from this application + /// + /// The cookie returned by the method + /// when the stack frame enumerator provider was added + void RemoveStackFrameSniffer( + [In] uint cookie + ); + + /// + /// Determines if the current running thread is the debugger thread + /// + /// The method returns an HRESULT + [PreserveSig] + uint QueryCurrentThreadIsDebuggerThread(); + + /// + /// Provides a mechanism for the caller to run code in the debugger thread + /// + /// The object to call + /// First parameter to pass to the IDebugThreadCall.ThreadCallHandler method + /// Second parameter to pass to the IDebugThreadCall.ThreadCallHandler method + /// Third parameter to pass to the IDebugThreadCall.ThreadCallHandler method + void SynchronousCallInDebuggerThread( + [In] [MarshalAs(UnmanagedType.Interface)] IDebugThreadCall64 call, + [In] ulong param1, + [In] ulong param2, + [In] ulong param3 + ); + + /// + /// Creates a new application node that is associated with a specific document provider + /// + /// The application node associated with this document provider + void CreateApplicationNode( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugApplicationNode node + ); + + /// + /// Fires a generic event to the debugger's interface + /// + /// A GUID for the object + /// An event object to pass to the debugger + void FireDebuggerEvent( + [In] ref Guid iid, + [In] [MarshalAs(UnmanagedType.IUnknown, IidParameterIndex = 0)] object eventObject + ); + + /// + /// Causes the current thread to block and sends a notification of the error to the debugger IDE + /// + /// The error that occurred + /// The script site of the thread + /// Action to take when the debugger resumes the application + /// Action to take when the debugger resumes the application + /// if there is an error + /// Flag which is true if the engine should call + /// the method + void HandleRuntimeError( + [In] [MarshalAs(UnmanagedType.Interface)] IActiveScriptErrorDebug errorDebug, + [In] [MarshalAs(UnmanagedType.Interface)] IActiveScriptSite scriptSite, + [Out] out BreakResumeAction breakResumeAction, + [Out] out ErrorResumeAction errorResumeAction, + [Out] [MarshalAs(UnmanagedType.Bool)] out bool callOnScriptError + ); + + /// + /// Determines if a JIT debugger is registered + /// + /// If the method succeeds and a JIT debugger is registered, the method returns true. + /// Otherwise, it returns false. + [PreserveSig] + [return: MarshalAs(UnmanagedType.Bool)] + bool FCanJitDebug(); + + /// + /// Determines if a JIT debugger is registered to auto-debug dumb hosts + /// + /// If the method succeeds and a JIT debugger is registered to auto-debug dumb hosts, + /// the method returns true. Otherwise, it returns false. + [PreserveSig] + [return: MarshalAs(UnmanagedType.Bool)] + bool FIsAutoJitDebugEnabled(); + + /// + /// Adds a global expression context provider to this application + /// + /// The global context provider to add to this application + /// A cookie that is used to remove this global expression context provider + /// from the application + void AddGlobalExpressionContextProvider( + [In] [MarshalAs(UnmanagedType.Interface)] IProvideExpressionContexts provider, + [Out] out uint cookie + ); + + /// + /// Removes a global expression context provider from this application + /// + /// The cookie returned by the method + /// when the global context provider was added + void RemoveGlobalExpressionContextProvider( + [In] uint cookie + ); +#endif + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugApplicationNode.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugApplicationNode.cs new file mode 100644 index 0000000..0b892ae --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugApplicationNode.cs @@ -0,0 +1,93 @@ +#if NETFRAMEWORK +using System; +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// The interface extends the functionality of + /// the interface by providing a context within a project tree + /// + [ComImport] + [Guid("51973c34-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugApplicationNode // : IDebugDocumentProvider + { + #region IDebugDocumentProvider methods + + #region IDebugDocumentInfo methods + + /// + /// Returns the specified document name + /// + /// The type of document name to return + /// String containing the name + void GetName( + [In] DocumentNameType type, + [Out] [MarshalAs(UnmanagedType.BStr)] out string name + ); + + /// + /// Returns a CLSID identifying the document type + /// + /// A CLSID identifying the document type + void GetDocumentClassId( + [Out] out Guid clsid + ); + + #endregion + + /// + /// Causes the document to be instantiated if it does not already exist + /// + /// The debug document corresponding to the document + void GetDocument( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugDocument document + ); + + #endregion + + /// + /// Enumerates the child nodes of this application node + /// + /// The enumeration of this node's child nodes + void EnumChildren( + [Out] [MarshalAs(UnmanagedType.Interface)] out IEnumDebugApplicationNodes enumNodes + ); + + /// + /// Returns the parent node of this application node + /// + /// Parent application node of this application node + void GetParent( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugApplicationNode node + ); + + /// + /// Sets the document provider for this application node + /// + /// The document provider for this application node + void SetDocumentProvider( + [In] [MarshalAs(UnmanagedType.Interface)] IDebugDocumentProvider provider + ); + + /// + /// Causes this application to release all references and enter an inactive state + /// + void Close(); + + /// + /// Adds this application node to the specified project tree + /// + /// The project tree where this application node is to be added + void Attach( + [In] [MarshalAs(UnmanagedType.Interface)] IDebugApplicationNode node + ); + + /// + /// Removes this application node from the project tree + /// + void Detach(); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugApplicationThread.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugApplicationThread.cs new file mode 100644 index 0000000..c8fc7de --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugApplicationThread.cs @@ -0,0 +1,17 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Allows language engines and hosts to provide thread synchronization and to maintain + /// thread-specific debug state information. This interface extends + /// the interface to provide non-remote access to the thread. + /// + [ComImport] + [Guid("51973c38-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugApplicationThread // : IRemoteDebugApplicationThread + { } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugAsyncOperation.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugAsyncOperation.cs new file mode 100644 index 0000000..68cfd16 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugAsyncOperation.cs @@ -0,0 +1,19 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// The Process Debug Manager implements the interface. + /// A language engine calls the IDebugApplication.CreateAsyncDebugOperation method + /// to obtain a reference to this interface. The language engine can use + /// the interface to provide asynchronous access to a synchronous + /// debug operation. + /// + [ComImport] + [Guid("51973c1b-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugAsyncOperation + { } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugCodeContext.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugCodeContext.cs new file mode 100644 index 0000000..18e0c86 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugCodeContext.cs @@ -0,0 +1,31 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// An abstraction that represents a position in executable code + /// + [ComImport] + [Guid("51973c13-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugCodeContext + { + /// + /// Returns the document context associated with this code context + /// + /// The document context associated with this code context + void GetDocumentContext( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugDocumentContext context + ); + + /// + /// Sets or clears a breakpoint at this code context + /// + /// Specifies the breakpoint state for this code context + void SetBreakPoint( + [In] BreakpointState state + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocument.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocument.cs new file mode 100644 index 0000000..e70f7f4 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocument.cs @@ -0,0 +1,38 @@ +#if NETFRAMEWORK +using System; +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// The base interface for all debug documents + /// + [ComImport] + [Guid("51973c21-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugDocument // : IDebugDocumentInfo + { + #region IDebugDocumentInfo methods + + /// + /// Returns the specified document name + /// + /// The type of document name to return + /// String containing the name + void GetName( + [In] DocumentNameType type, + [Out] [MarshalAs(UnmanagedType.BStr)] out string name + ); + + /// + /// Returns a CLSID identifying the document type + /// + /// A CLSID identifying the document type + void GetDocumentClassId( + [Out] out Guid clsid + ); + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentContext.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentContext.cs new file mode 100644 index 0000000..3fe7cc1 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentContext.cs @@ -0,0 +1,32 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Provides an abstract representation of a portion of the document being debugged. + /// For text documents, this representation consists of a character-position range + /// + [ComImport] + [Guid("51973c28-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugDocumentContext + { + /// + /// Returns the document that contains this context + /// + /// The document that contains this context + void GetDocument( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugDocument document + ); + + /// + /// Enumerates the code contexts associated with this document context + /// + /// The code contexts associated with this document context + void EnumCodeContexts( + [Out] [MarshalAs(UnmanagedType.Interface)] out IEnumDebugCodeContexts enumContexts + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentHelper32.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentHelper32.cs new file mode 100644 index 0000000..958df77 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentHelper32.cs @@ -0,0 +1,15 @@ +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Provide implementations for many interfaces necessary for smart hosting, such as + /// the IDebugDocument, IDebugDocumentContext, IDebugDocumentProvider, + /// IDebugDocumentText and IDebugDocumentTextEvents interfaces + /// + [ComImport] + [Guid("51973C26-CB0C-11d0-B5C9-00A0244A0E7A")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugDocumentHelper32 + { } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentHelper64.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentHelper64.cs new file mode 100644 index 0000000..4925b95 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentHelper64.cs @@ -0,0 +1,15 @@ +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Provide implementations for many interfaces necessary for smart hosting, such as + /// the IDebugDocument, IDebugDocumentContext, IDebugDocumentProvider, + /// IDebugDocumentText and IDebugDocumentTextEvents interfaces + /// + [ComImport] + [Guid("c4c7363c-20fd-47f9-bd82-4855e0150871")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugDocumentHelper64 + { } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentInfo.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentInfo.cs new file mode 100644 index 0000000..488e9ea --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentInfo.cs @@ -0,0 +1,34 @@ +#if NETFRAMEWORK +using System; +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Provides information on a document, which may or may not be instantiated + /// + [ComImport] + [Guid("51973c1f-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugDocumentInfo + { + /// + /// Returns the specified document name + /// + /// The type of document name to return + /// String containing the name + void GetName( + [In] DocumentNameType type, + [Out] [MarshalAs(UnmanagedType.BStr)] out string name + ); + + /// + /// Returns a CLSID identifying the document type + /// + /// A CLSID identifying the document type + void GetDocumentClassId( + [Out] out Guid clsid + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentProvider.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentProvider.cs new file mode 100644 index 0000000..c749b6a --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentProvider.cs @@ -0,0 +1,46 @@ +#if NETFRAMEWORK +using System; +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Provides the means for instantiating a document on demand + /// + [ComImport] + [Guid("51973c20-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugDocumentProvider // : IDebugDocumentInfo + { + #region IDebugDocumentInfo methods + + /// + /// Returns the specified document name + /// + /// The type of document name to return + /// String containing the name + void GetName( + [In] DocumentNameType type, + [Out] [MarshalAs(UnmanagedType.BStr)] out string name + ); + + /// + /// Returns a CLSID identifying the document type + /// + /// A CLSID identifying the document type + void GetDocumentClassId( + [Out] out Guid clsid + ); + + #endregion + + /// + /// Causes the document to be instantiated if it does not already exist + /// + /// The debug document corresponding to the document + void GetDocument( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugDocument document + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentText.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentText.cs new file mode 100644 index 0000000..af74329 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugDocumentText.cs @@ -0,0 +1,132 @@ +#if NETFRAMEWORK +using System; +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Provides access to a text-only version of the debug document + /// + [ComImport] + [Guid("51973c22-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugDocumentText // : IDebugDocument + { + #region IDebugDocument methods + + #region IDebugDocumentInfo methods + + /// + /// Returns the specified document name + /// + /// The type of document name to return + /// String containing the name + void GetName( + [In] DocumentNameType type, + [Out] [MarshalAs(UnmanagedType.BStr)] out string name + ); + + /// + /// Returns a CLSID identifying the document type + /// + /// A CLSID identifying the document type + void GetDocumentClassId( + [Out] out Guid clsid + ); + + #endregion + + #endregion + + /// + /// Returns the attributes of the document + /// + /// The text attributes of the document + void GetDocumentAttributes( + [Out] out TextDocAttrs attrs + ); + + /// + /// Returns the number of lines and number of characters in the document + /// + /// Number of lines in the document. If this parameter is null, + /// the method does not return a value + /// Number of characters in the document. If this parameter is null, + /// the method does not return a value + void GetSize( + [Out] out uint numLines, + [Out] out uint length + ); + + /// + /// Returns the character-position corresponding to the first character of a line + /// + /// The line number + /// The character position within the document of the start of + /// line + void GetPositionOfLine( + [In] uint lineNumber, + [Out] out uint position + ); + + /// + /// Returns the line number and, optionally, the character offset within the line that corresponds + /// to the given character-position + /// + /// Start location of the character position range + /// The line number of the range + /// The character offset of the range within line . + /// If this parameter is null, the method does not return a value + void GetLineOfPosition( + [In] uint position, + [Out] out uint lineNumber, + [Out] out uint offsetInLine + ); + + /// + /// Retrieves the characters and/or the character attributes associated with a character-position range + /// + /// Start location of the character position range + /// A character text buffer. The buffer must be large enough to hold + /// characters. If this parameter is null, the method does not return characters. + /// A character attribute buffer. The buffer must be large enough to hold + /// characters. If this parameter is null, the method does not return attributes. + /// The number of characters/attributes returned. This parameter must be set to + /// zero before calling this method. + /// Number of characters in the character position range. Also specifies + /// the maximum number of characters to return. + void GetText( + [In] uint position, + [In] IntPtr pChars, + [In] IntPtr pAttrs, + [In] [Out] ref uint length, + [In] uint maxChars + ); + + /// + /// Returns the character-position range corresponding to a document context + /// + /// The document context object + /// Start location of the character position range + /// Number of characters in the range + void GetPositionOfContext( + [In] [MarshalAs(UnmanagedType.Interface)] IDebugDocumentContext context, + [Out] out uint position, + [Out] out uint length + ); + + /// + /// Creates a document context object corresponding to the provided character position range + /// + /// Start location of the character position range + /// Number of characters in the range + /// The document context object corresponding to the specified character + /// position range + void GetContextOfPosition( + [In] uint position, + [In] uint length, + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugDocumentContext context + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugProperty.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugProperty.cs new file mode 100644 index 0000000..7e8df16 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugProperty.cs @@ -0,0 +1,17 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Used to describe any hierarchical property of the entity being debugged that has a name, type, and value. + /// Most commonly, is used to describe the result of expression evaluation, + /// statement evaluation, or register evaluation. + /// + [ComImport] + [Guid("51973c50-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugProperty + { } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugStackFrame.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugStackFrame.cs new file mode 100644 index 0000000..9c62495 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugStackFrame.cs @@ -0,0 +1,61 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Represents a logical stack frame on the thread stack + /// + [ComImport] + [Guid("51973c17-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugStackFrame + { + /// + /// Returns the current code context associated with the stack frame + /// + /// The code context associated with the stack frame + void GetCodeContext( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugCodeContext context + ); + + /// + /// Returns a short or long textual description of the stack frame + /// + /// Flag, where true returns a long description and + /// false returns a short description + /// The description of the stack frame + void GetDescriptionString( + [In] [MarshalAs(UnmanagedType.Bool)] bool longString, + [Out] [MarshalAs(UnmanagedType.BStr)] out string description + ); + + /// + /// Returns a short or long textual description of the language + /// + /// Flag, where true returns a long description and + /// false returns a short description + /// The description of the language + void GetLanguageString( + [In] [MarshalAs(UnmanagedType.Bool)] bool longString, + [Out] [MarshalAs(UnmanagedType.BStr)] out string language + ); + + /// + /// Returns the thread associated with this stack frame + /// + /// The thread associated with this stack frame + void GetThread( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugApplicationThread thread + ); + + /// + /// Returns a property browser for the current frame + /// + /// A property browser for the current frame + void GetDebugProperty( + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugProperty property + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugStackFrameSniffer.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugStackFrameSniffer.cs new file mode 100644 index 0000000..e107105 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugStackFrameSniffer.cs @@ -0,0 +1,25 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Provides a way to enumerate the logical stack frames known by a component. Script engines typically + /// implement this interface. The process debug manager uses this interface to find all stack frames + /// associated with a given thread. + /// + [ComImport] + [Guid("51973c18-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugStackFrameSniffer + { + /// + /// Returns an enumerator of stack frames for the current thread + /// + /// Enumerator of stack frames for the current thread + void EnumStackFrames( + [Out] [MarshalAs(UnmanagedType.Interface)] out IEnumDebugStackFrames enumFrames + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugStackFrameSnifferEx32.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugStackFrameSnifferEx32.cs new file mode 100644 index 0000000..8c301ab --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugStackFrameSnifferEx32.cs @@ -0,0 +1,34 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Provides a way to enumerate the logical stack frames known by a component. Script engines typically + /// implement this interface. The process debug manager uses this interface to find all stack frames + /// associated with a given thread. + /// + [ComImport] + [Guid("51973c19-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugStackFrameSnifferEx32 // : IDebugStackFrameSniffer + { + #region IDebugStackFrameSniffer methods + + /// + /// Returns an enumerator of stack frames for the current thread + /// + /// Enumerator of stack frames for the current thread + void EnumStackFrames( + [Out] [MarshalAs(UnmanagedType.Interface)] out IEnumDebugStackFrames enumFrames + ); + + #endregion + + void EnumStackFramesEx32( + [In] uint minimum, + [Out] [MarshalAs(UnmanagedType.Interface)] out IEnumDebugStackFrames enumFrames + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugStackFrameSnifferEx64.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugStackFrameSnifferEx64.cs new file mode 100644 index 0000000..3aa6d91 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugStackFrameSnifferEx64.cs @@ -0,0 +1,34 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Provides a way to enumerate the logical stack frames known by a component. Script engines typically + /// implement this interface. The process debug manager uses this interface to find all stack frames + /// associated with a given thread. + /// + [ComImport] + [Guid("8cd12af4-49c1-4d52-8d8a-c146f47581aa")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugStackFrameSnifferEx64 // : IDebugStackFrameSniffer + { + #region IDebugStackFrameSniffer methods + + /// + /// Returns an enumerator of stack frames for the current thread + /// + /// Enumerator of stack frames for the current thread + void EnumStackFrames( + [Out] [MarshalAs(UnmanagedType.Interface)] out IEnumDebugStackFrames enumFrames + ); + + #endregion + + void EnumStackFramesEx64( + [In] ulong minimum, + [Out] [MarshalAs(UnmanagedType.Interface)] out IEnumDebugStackFrames enumFrames + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugSyncOperation.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugSyncOperation.cs new file mode 100644 index 0000000..64c872f --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugSyncOperation.cs @@ -0,0 +1,17 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Allows a script engine to abstract an operation (such as expression evaluation) that needs to be + /// performed while nested in a particular blocked thread. The interface also provides a mechanism for + /// canceling unresponsive operations. + /// + [ComImport] + [Guid("51973c1a-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugSyncOperation + { } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugThreadCall32.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugThreadCall32.cs new file mode 100644 index 0000000..270c035 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugThreadCall32.cs @@ -0,0 +1,17 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// The interface is typically implemented by a component that makes + /// cross-thread calls with the IDebugThread marshalling implementation provided by + /// the process debug manager (PDM). + /// + [ComImport] + [Guid("51973c36-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugThreadCall32 + { } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugThreadCall64.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugThreadCall64.cs new file mode 100644 index 0000000..2a87b8e --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IDebugThreadCall64.cs @@ -0,0 +1,17 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// The interface is typically implemented by a component that makes + /// cross-thread calls with the IDebugThread marshalling implementation provided by + /// the process debug manager (PDM). + /// + [ComImport] + [Guid("cb3fa335-e979-42fd-9fcf-a7546a0f3905")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IDebugThreadCall64 + { } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IEnumDebugApplicationNodes.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IEnumDebugApplicationNodes.cs new file mode 100644 index 0000000..7c311fe --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IEnumDebugApplicationNodes.cs @@ -0,0 +1,15 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Enumerates child nodes of a node associated with an application + /// + [ComImport] + [Guid("51973c3a-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IEnumDebugApplicationNodes + { } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IEnumDebugCodeContexts.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IEnumDebugCodeContexts.cs new file mode 100644 index 0000000..93bb198 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IEnumDebugCodeContexts.cs @@ -0,0 +1,50 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Enumerates the code contexts that correspond to a document context + /// + [ComImport] + [Guid("51973c1d-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IEnumDebugCodeContexts + { + /// + /// Retrieves a specified number of segments in the enumeration sequence + /// + /// The number of segments to retrieve + /// Returns an array of interfaces that + /// represents the segments being retrieved + /// The actual number of segments fetched by the enumerator + void Next( + [In] uint count, + [Out] [MarshalAs(UnmanagedType.Interface)] out IDebugCodeContext[] contexts, + [Out] out uint countFetched + ); + + /// + /// Skips a specified number of segments in an enumeration sequence + /// + /// Number of segments in the enumeration sequence to skip + void Skip( + [In] uint count + ); + + /// + /// Resets an enumeration sequence to the beginning + /// + void Reset(); + + /// + /// Creates an enumerator that contains the same state as the current enumerator + /// + /// Returns the interface of + /// the clone of the enumerator + void Clone( + [Out] [MarshalAs(UnmanagedType.Interface)] out IEnumDebugCodeContexts enumContexts + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IEnumDebugExpressionContexts.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IEnumDebugExpressionContexts.cs new file mode 100644 index 0000000..fcc7d9d --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IEnumDebugExpressionContexts.cs @@ -0,0 +1,15 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Enumerates a collection of IDebugExpressionContexts objects + /// + [ComImport] + [Guid("51973c40-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IEnumDebugExpressionContexts + { } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IEnumDebugStackFrames.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IEnumDebugStackFrames.cs new file mode 100644 index 0000000..6c75645 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IEnumDebugStackFrames.cs @@ -0,0 +1,50 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Enumerates the stack frames corresponding to a thread + /// + [ComImport] + [Guid("51973c1e-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IEnumDebugStackFrames + { + /// + /// Retrieves a specified number of segments in the enumeration sequence + /// + /// The number of segments to retrieve + /// Returns an array of + /// interfaces that represents the segments being retrieved + /// The actual number of segments fetched by the enumerator + void Next( + [In] uint count, + [Out] out DebugStackFrameDescriptor descriptor, + [Out] out uint countFetched + ); + + /// + /// Skips a specified number of segments in an enumeration sequence + /// + /// Number of segments in the enumeration sequence to skip + void Skip( + [In] uint count + ); + + /// + /// Resets an enumeration sequence to the beginning + /// + void Reset(); + + /// + /// Creates an enumerator that contains the same state as the current enumerator + /// + /// Returns the interface of + /// the clone of the enumerator + void Clone( + [Out] [MarshalAs(UnmanagedType.Interface)] out IEnumDebugStackFrames enumFrames + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IEnumRemoteDebugApplicationThreads.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IEnumRemoteDebugApplicationThreads.cs new file mode 100644 index 0000000..e4121a9 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IEnumRemoteDebugApplicationThreads.cs @@ -0,0 +1,15 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Enumerates the running threads in an application + /// + [ComImport] + [Guid("51973c3c-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IEnumRemoteDebugApplicationThreads + { } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/IProcessDebugManager32.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IProcessDebugManager32.cs similarity index 77% rename from src/MsieJavaScriptEngine/JsRt/Ie/IProcessDebugManager32.cs rename to src/MsieJavaScriptEngine/ActiveScript/Debugging/IProcessDebugManager32.cs index fee3744..1983b60 100644 --- a/src/MsieJavaScriptEngine/JsRt/Ie/IProcessDebugManager32.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IProcessDebugManager32.cs @@ -1,10 +1,13 @@ -namespace MsieJavaScriptEngine.JsRt.Ie -{ - using System.Runtime.InteropServices; +using System.Runtime.InteropServices; +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ /// - /// IProcessDebugManager32 COM interface + /// Primary interface to the process debug manager. This interface can create, add, or + /// remove a virtual application from a process. It can enumerate stack frames and + /// application threads. /// + [ComImport] [Guid("51973C2f-CB0C-11d0-B5C9-00A0244A0E7A")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IProcessDebugManager32 @@ -26,7 +29,7 @@ internal interface IProcessDebugManager32 /// /// The new debug application /// An engine-defined cookie - void AddApplication(IDebugApplication32 debugApplication, out uint cookie); + uint AddApplication(IDebugApplication32 debugApplication, out uint cookie); /// /// Removes a debug application diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/IProcessDebugManager64.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IProcessDebugManager64.cs similarity index 77% rename from src/MsieJavaScriptEngine/JsRt/Ie/IProcessDebugManager64.cs rename to src/MsieJavaScriptEngine/ActiveScript/Debugging/IProcessDebugManager64.cs index 413576f..f572297 100644 --- a/src/MsieJavaScriptEngine/JsRt/Ie/IProcessDebugManager64.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IProcessDebugManager64.cs @@ -1,10 +1,13 @@ -namespace MsieJavaScriptEngine.JsRt.Ie -{ - using System.Runtime.InteropServices; +using System.Runtime.InteropServices; +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ /// - /// IProcessDebugManager64 COM interface + /// Primary interface to the process debug manager. This interface can create, add, or + /// remove a virtual application from a process. It can enumerate stack frames and + /// application threads. /// + [ComImport] [Guid("56b9fC1C-63A9-4CC1-AC21-087D69A17FAB")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IProcessDebugManager64 @@ -26,7 +29,7 @@ internal interface IProcessDebugManager64 /// /// The new debug application /// An engine-defined cookie - void AddApplication(IDebugApplication64 debugApplication, out uint cookie); + uint AddApplication(IDebugApplication64 debugApplication, out uint cookie); /// /// Removes a debug application diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IProvideExpressionContexts.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IProvideExpressionContexts.cs new file mode 100644 index 0000000..de68303 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IProvideExpressionContexts.cs @@ -0,0 +1,15 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Provides a way to enumerate expression contexts known by a certain component + /// + [ComImport] + [Guid("51973c41-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IProvideExpressionContexts + { } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/IRemoteDebugApplicationThread.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IRemoteDebugApplicationThread.cs new file mode 100644 index 0000000..0cd3e26 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/IRemoteDebugApplicationThread.cs @@ -0,0 +1,15 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Represents a thread of execution within a particular application + /// + [ComImport] + [Guid("51973c37-cb0c-11d0-b5c9-00a0244a0e7a")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IRemoteDebugApplicationThread + { } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/NullEnumDebugStackFrames.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/NullEnumDebugStackFrames.cs new file mode 100644 index 0000000..5cb29a8 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/NullEnumDebugStackFrames.cs @@ -0,0 +1,30 @@ +#if NETFRAMEWORK +using System; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + internal class NullEnumDebugStackFrames : IEnumDebugStackFrames + { + #region IEnumDebugStackFrames implementation + + public void Next(uint count, out DebugStackFrameDescriptor descriptor, out uint countFetched) + { + descriptor = default(DebugStackFrameDescriptor); + countFetched = 0; + } + + public void Skip(uint count) + { } + + public void Reset() + { } + + public void Clone(out IEnumDebugStackFrames enumFrames) + { + throw new NotImplementedException(); + } + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/ProcessDebugManager.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/ProcessDebugManager.cs similarity index 52% rename from src/MsieJavaScriptEngine/JsRt/Ie/ProcessDebugManager.cs rename to src/MsieJavaScriptEngine/ActiveScript/Debugging/ProcessDebugManager.cs index 602032b..2550fa9 100644 --- a/src/MsieJavaScriptEngine/JsRt/Ie/ProcessDebugManager.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/ProcessDebugManager.cs @@ -1,9 +1,9 @@ -namespace MsieJavaScriptEngine.JsRt.Ie -{ - using System.Runtime.InteropServices; +using System.Runtime.InteropServices; +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ /// - /// ProcessDebugManager COM interface + /// Process debug manager /// [ComImport] [Guid("78A51822-51F4-11D0-8F20-00805F2CD064")] diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/ProcessDebugManagerWrapper.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/ProcessDebugManagerWrapper.cs new file mode 100644 index 0000000..8ae8f24 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/ProcessDebugManagerWrapper.cs @@ -0,0 +1,145 @@ +#if NETFRAMEWORK +using MsieJavaScriptEngine.Helpers; +using MsieJavaScriptEngine.Utilities; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Wrapper for process debug manager + /// + internal sealed class ProcessDebugManagerWrapper + { + /// + /// Flag that the current process debug manager is a 64-bit + /// + private readonly bool _is64Bit; + + /// + /// Instance of 32-bit process debug manager + /// + private readonly IProcessDebugManager32 _processDebugManager32; + + /// + /// Instance of 64-bit process debug manager + /// + private readonly IProcessDebugManager64 _processDebugManager64; + + + /// + /// Constructs an instance of the wrapper for process debug manager + /// + /// Instance of 32-bit process debug manager + public ProcessDebugManagerWrapper(IProcessDebugManager32 processDebugManager) + { + _is64Bit = false; + _processDebugManager32 = processDebugManager; + } + + /// + /// Constructs an instance of the wrapper for process debug manager + /// + /// Instance of 64-bit process debug manager + public ProcessDebugManagerWrapper(IProcessDebugManager64 processDebugManager) + { + _is64Bit = true; + _processDebugManager64 = processDebugManager; + } + + + /// + /// Creates a wrapper for the process debug manager. A return value indicates whether + /// the creation succeeded. + /// + /// Wrapper for process debug manager + /// true if the wrapper was created successfully; otherwise, false. + public static bool TryCreate(out ProcessDebugManagerWrapper wrapper) + { + const string progId = "ProcessDebugManager"; + + if (Utils.Is64BitProcess()) + { + IProcessDebugManager64 processDebugManager64; + if (ComHelpers.TryCreateComObject(progId, null, out processDebugManager64)) + { + wrapper = new ProcessDebugManagerWrapper(processDebugManager64); + return true; + } + } + else + { + IProcessDebugManager32 processDebugManager32; + if (ComHelpers.TryCreateComObject(progId, null, out processDebugManager32)) + { + wrapper = new ProcessDebugManagerWrapper(processDebugManager32); + return true; + } + } + + wrapper = null; + return false; + } + + /// + /// Creates a wrapper for new debug application + /// + /// The wrapper for new debug application + public void CreateApplication(out DebugApplicationWrapper applicationWrapper) + { + if (_is64Bit) + { + IDebugApplication64 debugApplication64; + _processDebugManager64.CreateApplication(out debugApplication64); + applicationWrapper = new DebugApplicationWrapper(debugApplication64); + } + else + { + IDebugApplication32 debugApplication32; + _processDebugManager32.CreateApplication(out debugApplication32); + applicationWrapper = new DebugApplicationWrapper(debugApplication32); + } + } + + /// + /// Adds a new debug application. A return value indicates whether the adding succeeded. + /// + /// Wrapper for debug application + /// An engine-defined cookie + /// true if the debug application was added successfully; otherwise, false. + public bool TryAddApplication(DebugApplicationWrapper applicationWrapper, out uint cookie) + { + uint result; + + if (_is64Bit) + { + IDebugApplication64 application64 = applicationWrapper.DebugApplication64; + result = _processDebugManager64.AddApplication(application64, out cookie); + } + else + { + IDebugApplication32 application32 = applicationWrapper.DebugApplication32; + result = _processDebugManager32.AddApplication(application32, out cookie); + } + + bool isSucceeded = ComHelpers.HResult.Succeeded(result); + + return isSucceeded; + } + + /// + /// Removes a debug application + /// + /// The cookie of the debug application to remove + public void RemoveApplication(uint cookie) + { + if (_is64Bit) + { + _processDebugManager64.RemoveApplication(cookie); + } + else + { + _processDebugManager32.RemoveApplication(cookie); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/SourceTextAttrs.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/SourceTextAttrs.cs new file mode 100644 index 0000000..149e0d2 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/SourceTextAttrs.cs @@ -0,0 +1,55 @@ +#if NETFRAMEWORK +using System; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Describe the attributes of a single character of source text + /// + [Flags] + internal enum SourceTextAttrs : ushort + { + None = 0, + + /// + /// The character is part of a language keyword + /// (for example, the JavaScript keyword while) + /// + Keyword = 0x0001, + + /// + /// The character is part of a comment block + /// + Comment = 0x0002, + + /// + /// The character is not part of compiled language source text + /// (for example, the HTML surrounding a script block) + /// + NonSource = 0x0004, + + /// + /// The character is part of a language operator + /// (for example, the arithmetic operator +) + /// + Operator = 0x0008, + + /// + /// The character is part of a language numeric constant + /// (for example, the constant 3.14159) + /// + Number = 0x0010, + + /// + /// The character is part of a language string constant + /// (for example, the string "Hello World") + /// + String = 0x0020, + + /// + /// The character indicates the start of a function block + /// + FunctionStart = 0x0040 + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/Debugging/TextDocAttrs.cs b/src/MsieJavaScriptEngine/ActiveScript/Debugging/TextDocAttrs.cs new file mode 100644 index 0000000..a402865 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/Debugging/TextDocAttrs.cs @@ -0,0 +1,35 @@ +#if NETFRAMEWORK +using System; + +namespace MsieJavaScriptEngine.ActiveScript.Debugging +{ + /// + /// Describe the attributes of the document + /// + [Flags] + internal enum TextDocAttrs : uint + { + None = 0, + + /// + /// The document is read-only + /// + ReadOnly = 0x00000001, + + /// + /// The document is the primary file of this document tree + /// + TypePrimary = 0x00000002, + + /// + /// The document is a worker + /// + TypeWorker = 0x00000004, + + /// + /// The document is a script file + /// + TypeScript = 0x00000008 + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/HostItemBase.cs b/src/MsieJavaScriptEngine/ActiveScript/HostItemBase.cs new file mode 100644 index 0000000..1df3cf7 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/HostItemBase.cs @@ -0,0 +1,318 @@ +#if NETFRAMEWORK +using System; +#if NET45_OR_GREATER +using System.Buffers; +#endif +using System.Globalization; +using System.Reflection; +#if NET40 + +using PolyfillsForOldDotNet.System.Buffers; +#endif + +using MsieJavaScriptEngine.Helpers; + +namespace MsieJavaScriptEngine.ActiveScript +{ + /// + /// Base class of item, that implements interface + /// + internal abstract class HostItemBase : IReflect + { + /// + /// Target type + /// + protected readonly Type _type; + + /// + /// Target object + /// + protected readonly object _target; + + /// + /// JS engine mode + /// + protected readonly JsEngineMode _engineMode; + + /// + /// Flag for whether to allow the usage of reflection API in the script code + /// + protected readonly bool _allowReflection; + + /// + /// List of fields + /// + private readonly FieldInfo[] _fields; + + /// + /// List of properties + /// + private readonly PropertyInfo[] _properties; + + /// + /// List of methods + /// + private readonly MethodInfo[] _methods; + + /// + /// Gets a target object + /// + public object Target + { + get { return _target; } + } + + + /// + /// Constructs an instance of the wrapper for item, that implements interface + /// + /// Target type + /// Target object + /// JS engine mode + /// Flag for whether to allow the usage of reflection API in the script code + /// Flag for whether to allow access to members of the instance + protected HostItemBase(Type type, object target, JsEngineMode engineMode, bool allowReflection, bool instance) + { + _type = type; + _target = target; + _allowReflection = allowReflection; + _engineMode = engineMode; + + BindingFlags defaultBindingFlags = ReflectionHelpers.GetDefaultBindingFlags(instance); + FieldInfo[] fields = _type.GetFields(defaultBindingFlags); + PropertyInfo[] properties = _type.GetProperties(defaultBindingFlags); + if (properties.Length > 0 && !allowReflection) + { + properties = GetAvailableProperties(properties); + } + MethodInfo[] methods = _type.GetMethods(defaultBindingFlags); + if (methods.Length > 0 && (properties.Length > 0 || !allowReflection)) + { + methods = GetAvailableMethods(methods, allowReflection); + } + + _fields = fields; + _properties = properties; + _methods = methods; + } + + + private static PropertyInfo[] GetAvailableProperties(PropertyInfo[] properties) + { + int propertyCount = properties.Length; + PropertyInfo[] availableProperties = null; + int availablePropertyCount = 0; + + var propertyArrayPool = ArrayPool.Shared; + PropertyInfo[] buffer = propertyArrayPool.Rent(propertyCount); + + try + { + foreach (PropertyInfo property in properties) + { + if (ReflectionHelpers.IsAllowedProperty(property)) + { + availablePropertyCount++; + + int availablePropertyIndex = availablePropertyCount - 1; + buffer[availablePropertyIndex] = property; + } + } + + if (availablePropertyCount < propertyCount) + { + if (availablePropertyCount == 0) + { + return []; + } + + availableProperties = new PropertyInfo[availablePropertyCount]; + Array.Copy(buffer, availableProperties, availablePropertyCount); + } + else + { + availableProperties = properties; + } + } + finally + { + bool clearArray = availablePropertyCount > 0; + propertyArrayPool.Return(buffer, clearArray); + } + + return availableProperties; + } + + private static MethodInfo[] GetAvailableMethods(MethodInfo[] methods, bool allowReflection) + { + int methodCount = methods.Length; + MethodInfo[] availableMethods = null; + int availableMethodCount = 0; + + var methodArrayPool = ArrayPool.Shared; + MethodInfo[] buffer = methodArrayPool.Rent(methodCount); + + try + { + foreach (MethodInfo method in methods) + { + if (ReflectionHelpers.IsFullyFledgedMethod(method) + && (allowReflection || ReflectionHelpers.IsAllowedMethod(method))) + { + availableMethodCount++; + + int availableMethodIndex = availableMethodCount - 1; + buffer[availableMethodIndex] = method; + } + } + + if (availableMethodCount < methodCount) + { + if (availableMethodCount == 0) + { + return []; + } + + availableMethods = new MethodInfo[availableMethodCount]; + Array.Copy(buffer, availableMethods, availableMethodCount); + } + else + { + availableMethods = methods; + } + } + finally + { + bool clearArray = availableMethodCount > 0; + methodArrayPool.Return(buffer, clearArray); + } + + return availableMethods; + } + + private FieldInfo GetField(string name) + { + foreach (FieldInfo field in _fields) + { + if (field.Name.Equals(name, StringComparison.Ordinal)) + { + return field; + } + } + + return null; + } + + protected abstract object InnerInvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, + object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters); + + protected object InvokeStandardMember(string name, BindingFlags invokeAttr, Binder binder, object target, + object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) + { + BindingFlags processedInvokeAttr = invokeAttr; + if (processedInvokeAttr.HasFlag(BindingFlags.GetProperty) + || processedInvokeAttr.HasFlag(BindingFlags.SetProperty) + || processedInvokeAttr.HasFlag(BindingFlags.PutDispProperty)) + { + FieldInfo field = GetField(name); + if (field is not null) + { + if (processedInvokeAttr.HasFlag(BindingFlags.GetProperty)) + { + processedInvokeAttr &= ~BindingFlags.GetProperty; + processedInvokeAttr |= BindingFlags.GetField; + } + else if (processedInvokeAttr.HasFlag(BindingFlags.SetProperty)) + { + processedInvokeAttr &= ~BindingFlags.SetProperty; + processedInvokeAttr |= BindingFlags.SetField; + } + else if (processedInvokeAttr.HasFlag(BindingFlags.PutDispProperty)) + { + if (field.IsInitOnly) + { + // Prevents a setting of value to the read-only field + return null; + } + + processedInvokeAttr &= ~BindingFlags.PutDispProperty; + processedInvokeAttr |= BindingFlags.SetField; + } + } + } + + object result = _type.InvokeMember(name, processedInvokeAttr, binder, target, + args, modifiers, culture, namedParameters); + + return result; + } + + #region IReflect implementation + + Type IReflect.UnderlyingSystemType + { + get { throw new NotImplementedException(); } + } + + + FieldInfo IReflect.GetField(string name, BindingFlags bindingAttr) + { + throw new NotImplementedException(); + } + + FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr) + { + return _fields; + } + + MemberInfo[] IReflect.GetMember(string name, BindingFlags bindingAttr) + { + throw new NotImplementedException(); + } + + MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr) + { + throw new NotImplementedException(); + } + + MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr) + { + throw new NotImplementedException(); + } + + MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) + { + throw new NotImplementedException(); + } + + MethodInfo[] IReflect.GetMethods(BindingFlags bindingAttr) + { + return _methods; + } + + PropertyInfo[] IReflect.GetProperties(BindingFlags bindingAttr) + { + return _properties; + } + + PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr) + { + throw new NotImplementedException(); + } + + PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr, Binder binder, + Type returnType, Type[] types, ParameterModifier[] modifiers) + { + throw new NotImplementedException(); + } + + object IReflect.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, + object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) + { + return InnerInvokeMember(name, invokeAttr, binder,target, args, modifiers, culture, namedParameters); + } + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/HostObject.cs b/src/MsieJavaScriptEngine/ActiveScript/HostObject.cs new file mode 100644 index 0000000..ea4dce8 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/HostObject.cs @@ -0,0 +1,98 @@ +#if NETFRAMEWORK +using System; +using System.Globalization; +using System.Linq; +using System.Reflection; + +using MsieJavaScriptEngine.Constants; +using MsieJavaScriptEngine.Helpers; + +namespace MsieJavaScriptEngine.ActiveScript +{ + /// + /// Wrapper for object, that implements interface + /// + internal sealed class HostObject : HostItemBase + { + /// + /// Number of delegate parameters + /// + private int _delegateParameterCount = int.MinValue; + + + /// + /// Constructs an instance of the wrapper for object, that implements interface + /// + /// Target object + /// JS engine mode + /// Flag for whether to allow the usage of reflection API in the script code + public HostObject(object target, JsEngineMode engineMode, bool allowReflection) + : base(target.GetType(), target, engineMode, allowReflection, true) + { + var del = _target as Delegate; + if (del is not null) + { + _delegateParameterCount = del.Method.GetParameters().Length; + } + } + + + #region HostItemBase overrides + + protected override object InnerInvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, + object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) + { + object result; + object processedTarget = TypeMappingHelpers.MapToHostType(target); + + if (name == SpecialMemberName.Default && processedTarget is Delegate) + { + var del = (Delegate)processedTarget; + int argCount = args.Length; + int skippedArgCount = 0; + int parameterCount = _delegateParameterCount; + + if (_engineMode == JsEngineMode.Classic && argCount > 0 + && (argCount - parameterCount) > 0) + { + skippedArgCount = 1; + } + + int processedArgCount = argCount >= skippedArgCount ? argCount - skippedArgCount : 0; + if (processedArgCount > parameterCount) + { + processedArgCount = parameterCount; + } + + object[] processedArgs; + if (processedArgCount > 0) + { + processedArgs = args + .Skip(skippedArgCount) + .Take(processedArgCount) + .Select(TypeMappingHelpers.MapToHostType) + .ToArray() + ; + } + else + { + processedArgs = []; + } + + result = del.DynamicInvoke(processedArgs); + } + else + { + object[] processedArgs = TypeMappingHelpers.MapToHostType(args); + + result = InvokeStandardMember(name, invokeAttr, binder, processedTarget, + processedArgs, modifiers, culture, namedParameters); + } + + return TypeMappingHelpers.MapToScriptType(result, _engineMode, _allowReflection); + } + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/HostType.cs b/src/MsieJavaScriptEngine/ActiveScript/HostType.cs similarity index 59% rename from src/MsieJavaScriptEngine/HostType.cs rename to src/MsieJavaScriptEngine/ActiveScript/HostType.cs index 4cdbf31..855d02d 100644 --- a/src/MsieJavaScriptEngine/HostType.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/HostType.cs @@ -1,13 +1,14 @@ -namespace MsieJavaScriptEngine -{ - using System; - using System.Globalization; - using System.Linq; - using System.Reflection; +#if NETFRAMEWORK +using System; +using System.Globalization; +using System.Linq; +using System.Reflection; - using Constants; - using Helpers; +using MsieJavaScriptEngine.Constants; +using MsieJavaScriptEngine.Helpers; +namespace MsieJavaScriptEngine.ActiveScript +{ /// /// Wrapper for type, that implements interface /// @@ -17,38 +18,44 @@ internal sealed class HostType : HostItemBase /// Constructs an instance of the wrapper for type, that implements interface /// /// Target type - /// JavaScript engine mode - public HostType(Type type, JsEngineMode engineMode) - : base(type, null, engineMode, false) + /// JS engine mode + /// Flag for whether to allow the usage of reflection API in the script code + public HostType(Type type, JsEngineMode engineMode, bool allowReflection) + : base(type, null, engineMode, allowReflection, false) { } - #region HostItemBase implementation + #region HostItemBase overrides protected override object InnerInvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) { - object[] processedArgs = TypeMappingHelpers.MapToHostType(args); object result; if (name == SpecialMemberName.Default && invokeAttr.HasFlag(BindingFlags.CreateInstance)) { + object[] processedArgs = args; + if (_engineMode != JsEngineMode.Classic && processedArgs.Length > 0) { processedArgs = processedArgs.Skip(1).ToArray(); } + processedArgs = TypeMappingHelpers.MapToHostType(processedArgs); result = Activator.CreateInstance(_type, processedArgs); } else { + object[] processedArgs = TypeMappingHelpers.MapToHostType(args); + result = InvokeStandardMember(name, invokeAttr, binder, target, processedArgs, modifiers, culture, namedParameters); } - return TypeMappingHelpers.MapToScriptType(result, _engineMode); + return TypeMappingHelpers.MapToScriptType(result, _engineMode, _allowReflection); } #endregion } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/IActiveScript.cs b/src/MsieJavaScriptEngine/ActiveScript/IActiveScript.cs index 8fee946..4ce72e2 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/IActiveScript.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/IActiveScript.cs @@ -1,13 +1,15 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - using System; - using System.Runtime.InteropServices; +#if NETFRAMEWORK +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; - using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; +using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; +namespace MsieJavaScriptEngine.ActiveScript +{ /// - /// Provides the methods necessary to initialize the scripting engine. The scripting engine must - /// implement the IActiveScript interface + /// Provides the methods necessary to initialize the scripting engine. The scripting engine + /// must implement the interface. /// [ComImport] [Guid("bb1a2ae1-a4f9-11cf-8f20-00805f2cd064")] @@ -15,68 +17,76 @@ internal interface IActiveScript { /// - /// Informs the scripting engine of the IActiveScriptSite interface site provided by the host. - /// Call this method before any other IActiveScript interface methods is used + /// Informs the scripting engine of the interface site + /// provided by the host. Call this method before any other + /// interface methods is used. /// /// The host-supplied script site to be associated with this instance /// of the scripting engine. The site must be uniquely assigned to this scripting engine /// instance; it cannot be shared with other scripting engines. void SetScriptSite( - [In] IActiveScriptSite site); + [In] IActiveScriptSite site + ); /// - /// Retrieves the site object associated with the Windows Script engine + /// Retrieves the site object associated with the script engine /// /// Identifier of the requested interface /// The host's site object void GetScriptSite( [In] Guid iid, - [Out] [MarshalAs(UnmanagedType.IUnknown, IidParameterIndex = 0)] out IActiveScriptSite site); + [Out] [MarshalAs(UnmanagedType.IUnknown, IidParameterIndex = 0)] out IActiveScriptSite site + ); /// /// Puts the scripting engine into the given state. This method can be called from non-base - /// threads without resulting in a non-base callout to host objects or to the IActiveScriptSite - /// interface. + /// threads without resulting in a non-base callout to host objects or to the + /// interface. /// /// Sets the scripting engine to the given state void SetScriptState( - [In] ScriptState state); + [In] ScriptState state + ); /// /// Retrieves the current state of the scripting engine. This method can be called from /// non-base threads without resulting in a non-base callout to host objects or to the - /// IActiveScriptSite interface. + /// interface. /// /// The value indicates the current state of the scripting engine /// associated with the calling thread void GetScriptState( - [Out] out ScriptState state); + [Out] out ScriptState state + ); /// /// Causes the scripting engine to abandon any currently loaded script, lose its state, and /// release any interface pointers it has to other objects, thus entering a closed state. - /// Event sinks, immediately executed script text, and macro invocations that are already in - /// progress are completed before the state changes (use IActiveScript::InterruptScriptThread to - /// cancel a running script thread). This method must be called by the creating host before the - /// interface is released to prevent circular reference problems. + /// Event sinks, immediately executed script text, and macro invocations that are already + /// in progress are completed before the state changes (use + /// to cancel a running script thread). + /// This method must be called by the creating host before the interface is released to + /// prevent circular reference problems. /// void Close(); /// /// Adds the name of a root-level item to the scripting engine's name space. A root-level item - /// is an object with properties and methods, an event source, or all three + /// is an object with properties and methods, an event source, or all three. /// /// The name of the item as viewed from the script. The name must be unique /// and persistable /// Flags associated with an item void AddNamedItem( [In] [MarshalAs(UnmanagedType.LPWStr)] string name, - [In] ScriptItemFlags flags); + [In] ScriptItemFlags flags + ); /// - /// Adds a type library to the name space for the script. This is similar to the #include - /// directive in C/C++. It allows a set of predefined items such as class definitions, typedefs, - /// and named constants to be added to the run-time environment available to the script. + /// Adds a type library to the name space for the script. This is similar to the + /// #include directive in C/C++. It allows a set of predefined items such as + /// class definitions, typedefs, and named constants to be added to the run-time + /// environment available to the script. /// /// CLSID of the type library to add /// Major version number @@ -86,85 +96,91 @@ void AddTypeLib( [In] Guid clsId, [In] uint majorVersion, [In] uint minorVersion, - [In] ScriptTypeLibFlags typeLibFlags); + [In] ScriptTypeLibFlags typeLibFlags + ); /// - /// Retrieves the IDispatch interface for the methods and properties associated with the - /// currently running script + /// Retrieves the IDispatch interface for the methods and properties associated + /// with the currently running script /// /// The name of the item for which the caller needs the associated - /// dispatch object. If this parameter is NULL, the dispatch object contains as its members - /// all of the global methods and properties defined by the script. Through the IDispatch - /// interface and the associated ITypeInfo interface, the host can invoke script methods - /// or view and modify script variables. + /// dispatch object. If this parameter is null, the dispatch object contains as its members + /// all of the global methods and properties defined by the script. Through the + /// IDispatch interface and the associated interface, + /// the host can invoke script methods or view and modify script variables. /// The object associated with the script's global methods and - /// properties. If the scripting engine does not support such an object, NULL is returned. + /// properties. If the scripting engine does not support such an object, null is returned. void GetScriptDispatch( [In] [MarshalAs(UnmanagedType.LPWStr)] string itemName, - [Out] [MarshalAs(UnmanagedType.IDispatch)] out object dispatch); + [Out] [MarshalAs(UnmanagedType.IDispatch)] out object dispatch + ); /// /// Retrieves a scripting-engine-defined identifier for the currently executing thread. - /// The identifier can be used in subsequent calls to script thread execution-control - /// methods such as the IActiveScript.InterruptScriptThread method. + /// The identifier can be used in subsequent calls to script thread execution-control methods + /// such as the method. /// /// The script thread identifier associated with the current thread. - /// The interpretation of this identifier is left to the scripting engine, but it can be - /// just a copy of the Windows thread identifier. If the Win32 thread terminates, this - /// identifier becomes unassigned and can subsequently be assigned to another thread. + /// The interpretation of this identifier is left to the scripting engine, but it can be just + /// a copy of the Windows thread identifier. If the Win32 thread terminates, this identifier + /// becomes unassigned and can subsequently be assigned to another thread. void GetCurrentScriptThreadId( - [Out] out uint threadId); + [Out] out uint threadId + ); /// - /// Retrieves a scripting-engine-defined identifier for the thread associated with the - /// given Win32 thread + /// Retrieves a scripting-engine-defined identifier for the thread associated with the given + /// Win32 thread /// - /// Thread identifier of a running Win32 thread in the - /// current process. Use the IActiveScript::GetCurrentScriptThreadID function to - /// retrieve the thread identifier of the currently executing thread - /// The script thread identifier associated with the given - /// Win32 thread. The interpretation of this identifier is left to the scripting engine, - /// but it can be just a copy of the Windows thread identifier. Note that if the Win32 - /// thread terminates, this identifier becomes unassigned and may subsequently be - /// assigned to another thread. + /// Thread identifier of a running Win32 thread in the current + /// process. Use the function to retrieve + /// the thread identifier of the currently executing thread. + /// The script thread identifier associated with the given Win32 + /// thread. The interpretation of this identifier is left to the scripting engine, but it can + /// be just a copy of the Windows thread identifier. Note that if the Win32 thread terminates, + /// this identifier becomes unassigned and may subsequently be assigned to another thread. void GetScriptThreadId( [In] uint win32ThreadId, - [Out] out uint scriptThreadId); + [Out] out uint scriptThreadId + ); /// /// Retrieves the current state of a script thread /// /// Identifier of the thread for which the state is desired - /// Thread state + /// The state of the indicated thread void GetScriptThreadState( [In] uint scriptThreadId, - [Out] out ScriptThreadState threadState); + [Out] out ScriptThreadState threadState + ); /// - /// Interrupts the execution of a running script thread (an event sink, an immediate - /// execution, or a macro invocation). This method can be used to terminate a script that - /// is stuck (for example, in an infinite loop). It can be called from non-base threads - /// without resulting in a non-base callout to host objects or to the IActiveScriptSite method. + /// Interrupts the execution of a running script thread (an event sink, an immediate execution, + /// or a macro invocation). This method can be used to terminate a script that is stuck (for + /// example, in an infinite loop). It can be called from non-base threads without resulting in + /// a non-base callout to host objects or to the method. /// - /// Identifier of the thread to interrupt, or one of the - /// special thread identifier values - /// The error information that should be reported to the aborted script. + /// Identifier of the thread to interrupt + /// The error information that should be reported to the aborted script /// Option flags associated with the interruption void InterruptScriptThread( [In] uint scriptThreadId, - [In] EXCEPINFO exceptionInfo, - [In] ScriptInterruptFlags flags); + [In] ref EXCEPINFO exceptionInfo, + [In] ScriptInterruptFlags flags + ); /// /// Clones the current scripting engine (minus any current execution state), returning - /// a loaded scripting engine that has no site in the current thread. The properties of - /// this new scripting engine will be identical to the properties the original scripting + /// a loaded scripting engine that has no site in the current thread. The properties + /// of this new scripting engine will be identical to the properties the original scripting /// engine would be in if it were transitioned back to the initialized state. /// - /// The cloned scripting engine. The host must create a site and - /// call the IActiveScript.SetScriptSite method on the new scripting engine before it + /// The cloned scripting engine. The host must create a site and call + /// the method on the new scripting engine before it /// will be in the initialized state and, therefore, usable. void Clone( - [Out] [MarshalAs(UnmanagedType.Interface)] out IActiveScript script); + [Out] [MarshalAs(UnmanagedType.Interface)] out IActiveScript script + ); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptError.cs b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptError.cs index 412d4bd..10d8244 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptError.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptError.cs @@ -1,11 +1,12 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - using System.Runtime.InteropServices; +#if NETFRAMEWORK +using System.Runtime.InteropServices; - using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; +using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; +namespace MsieJavaScriptEngine.ActiveScript +{ /// - /// An object implementing this interface is passed to the IActiveScriptSite.OnScriptError method + /// An object implementing this interface is passed to the method /// whenever the scripting engine encounters an unhandled error. The host then calls methods on /// this object to obtain information about the error that occurred. /// @@ -18,13 +19,14 @@ internal interface IActiveScriptError /// Retrieves information about an error that occurred while the scripting engine was running /// a script /// - /// An EXCEPINFO structure that receives error information + /// An EXCEPINFO structure that receives error information void GetExceptionInfo( - [Out] out EXCEPINFO exceptionInfo); + [Out] out EXCEPINFO exceptionInfo + ); /// /// Retrieves the location in the source code where an error occurred while the scripting engine - /// was running a script. + /// was running a script /// /// A cookie that identifies the context. The interpretation of /// this parameter depends on the host application. @@ -33,7 +35,8 @@ void GetExceptionInfo( void GetSourcePosition( [Out] out uint sourceContext, [Out] out uint lineNumber, - [Out] out int characterPosition); + [Out] out int characterPosition + ); /// /// Retrieves the line in the source file where an error occurred while a scripting engine @@ -41,6 +44,8 @@ void GetSourcePosition( /// /// The line of source code in which the error occurred void GetSourceLineText( - [Out] [MarshalAs(UnmanagedType.BStr)] out string sourceLine); + [Out] [MarshalAs(UnmanagedType.BStr)] out string sourceLine + ); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptGarbageCollector.cs b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptGarbageCollector.cs new file mode 100644 index 0000000..937e164 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptGarbageCollector.cs @@ -0,0 +1,24 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript +{ + /// + /// Provides a method to start garbage collection. This interface should be implemented by + /// Active Script engines that want to clean up their resources. + /// + [ComImport] + [Guid("6aa2c4a0-2b53-11d4-a2a0-00104bd35090")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IActiveScriptGarbageCollector + { + /// + /// The Active Script host calls this method to start garbage collection + /// + /// The type of garbage collection + void CollectGarbage( + [In] ScriptGCType type + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptParse32.cs b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptParse32.cs index 919a818..39c5f0f 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptParse32.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptParse32.cs @@ -1,26 +1,23 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - using System; - using System.Runtime.InteropServices; +#if NETFRAMEWORK +using System; +using System.Runtime.InteropServices; - using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; +using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; +namespace MsieJavaScriptEngine.ActiveScript +{ /// - /// If the Windows Script engine allows raw text code scriptlets to be added to the script + /// If the Active Script engine allows raw text code scriptlets to be added to the script /// or allows expression text to be evaluated at run time, it implements the - /// IActiveScriptParse interface. For interpreted scripting languages that have no - /// independent authoring environment, such as VBScript, this provides an alternate - /// mechanism (other than IPersist*) to get script code into the scripting engine, and - /// to attach script fragments to various object events. + /// interface. /// /// - /// Before the scripting engine can be used, one of the following methods must be called - /// : IPersist*::Load, IPersist*::InitNew, or IActiveScriptParse::InitNew. The semantics - /// of this method are identical to IPersistStreamInit::InitNew, in that this method tells - /// the scripting engine to initialize itself. Note that it is not valid to call both - /// IPersist*::InitNew or IActiveScriptParse::InitNew and IPersist*::Load, nor is it valid - /// to call IPersist*::InitNew, IActiveScriptParse::InitNew, or IPersist*::Load more - /// than once. + /// Before the scripting engine can be used, one of the following methods must be called: + /// IPersist.Load, IPersist.InitNew, or . The semantics of + /// this method are identical to IPersistStreamInit.InitNew, in that this method tells the scripting + /// engine to initialize itself. Note that it is not valid to call both IPersist.InitNew or + /// and IPersist.Load, nor is it valid to call + /// IPersist.InitNew, , or IPersist.Load more than once. /// [ComImport] [Guid("bb1a2ae2-a4f9-11cf-8f20-00805f2cd064")] @@ -35,35 +32,36 @@ internal interface IActiveScriptParse32 /// /// Adds a code scriptlet to the script. This method is used in environments where the /// persistent state of the script is intertwined with the host document and the host - /// is responsible for restoring the script, rather than through an IPersist* interface. + /// is responsible for restoring the script, rather than through an IPersist interface. /// The primary examples are HTML scripting languages that allow scriptlets of code /// embedded in the HTML document to be attached to intrinsic events (for instance, - /// ONCLICK="button1.text='Exit'"). + /// ONCLICK="button1.text='Exit'"). /// /// The default name to associate with the scriptlet. If the - /// scriptlet does not contain naming information (as in the ONCLICK example above), - /// this name will be used to identify the scriptlet. If this parameter is NULL, the + /// scriptlet does not contain naming information (as in the ONCLICK example above), + /// this name will be used to identify the scriptlet. If this parameter is null, the /// scripting engine manufactures a unique name, if necessary. /// The scriptlet text to add. The interpretation of this string /// depends on the scripting language. /// The item name associated with this scriptlet. This parameter, - /// in addition to pstrSubItemName, identifies the object for which the scriptlet is - /// an event handler. + /// in addition to , identifies the object for which the + /// scriptlet is an event handler. /// The name of a subobject of the named item with which this /// scriptlet is associated; this name must be found in the named item's type - /// information. This parameter is NULL if the scriptlet is to be associated with the - /// named item instead of a subitem. This parameter, in addition to pstrItemName, - /// identifies the specific object for which the scriptlet is an event handler. + /// information. This parameter is null if the scriptlet is to be associated with the + /// named item instead of a subitem. This parameter, in addition to + /// , identifies the specific object for which the scriptlet + /// is an event handler. /// The name of the event for which the scriptlet is an event /// handler - /// The end-of-scriptlet delimiter. When the pstrCode parameter - /// is parsed from a stream of text, the host typically uses a delimiter, such as two - /// single quotation marks (''), to detect the end of the scriptlet. This parameter + /// The end-of-scriptlet delimiter. When the + /// parameter is parsed from a stream of text, the host typically uses a delimiter, such as + /// two single quotation marks (''), to detect the end of the scriptlet. This parameter /// specifies the delimiter that the host used, allowing the scripting engine to /// provide some conditional primitive preprocessing (for example, replacing a single /// quotation mark ['] with two single quotation marks for use as a delimiter). /// Exactly how (and if) the scripting engine makes use of this information depends - /// on the scripting engine. Set this parameter to NULL if the host did not use a + /// on the scripting engine. Set this parameter to null if the host did not use a /// delimiter to mark the end of the scriptlet. /// Application-defined value that is used for /// debugging purposes @@ -72,10 +70,10 @@ internal interface IActiveScriptParse32 /// Flags associated with the scriptlet /// Actual name used to identify the scriptlet. This is to be in /// order of preference: a name explicitly specified in the scriptlet text, the - /// default name provided in pstrDefaultName, or a unique name synthesized by the - /// scripting engine. + /// default name provided in , or a unique name + /// synthesized by the scripting engine. /// Exception information. This structure should be - /// filled in if DISP_E_EXCEPTION is returned + /// filled in if DISP_E_EXCEPTION is returned void AddScriptlet( [In] [MarshalAs(UnmanagedType.LPWStr)] string defaultName, [In] [MarshalAs(UnmanagedType.LPWStr)] string code, @@ -87,7 +85,8 @@ [In] [MarshalAs(UnmanagedType.LPWStr)] string delimiter, [In] uint startingLineNumber, [In] ScriptTextFlags flags, [Out] [MarshalAs(UnmanagedType.BStr)] out string name, - [Out] out EXCEPINFO exceptionInfo); + [Out] out EXCEPINFO exceptionInfo + ); /// /// Parses the given code scriptlet, adding declarations into the namespace and @@ -96,39 +95,42 @@ [Out] [MarshalAs(UnmanagedType.BStr)] out string name, /// The scriptlet text to evaluate. The interpretation of this /// string depends on the scripting language /// The item name that gives the context in which the - /// scriptlet is to be evaluated. If this parameter is NULL, the code is evaluated + /// scriptlet is to be evaluated. If this parameter is null, the code is evaluated /// in the scripting engine's global context /// The context object. This object is reserved for use in a /// debugging environment, where such a context may be provided by the debugger to - /// represent an active run-time context. If this parameter is NULL, the engine - /// uses pstrItemName to identify the context. - /// The end-of-scriptlet delimiter. When pstrCode is parsed - /// from a stream of text, the host typically uses a delimiter, such as two single - /// quotation marks (''), to detect the end of the scriptlet. This parameter specifies - /// the delimiter that the host used, allowing the scripting engine to provide some - /// conditional primitive preprocessing (for example, replacing a single quotation + /// represent an active run-time context. If this parameter is null, the engine + /// uses to identify the context. + /// The end-of-scriptlet delimiter. When + /// is parsed from a stream of text, the host typically uses a delimiter, such as two + /// single quotation marks (''), to detect the end of the scriptlet. This parameter + /// specifies the delimiter that the host used, allowing the scripting engine to provide + /// some conditional primitive preprocessing (for example, replacing a single quotation /// mark ['] with two single quotation marks for use as a delimiter). Exactly how /// (and if) the scripting engine makes use of this information depends on the - /// scripting engine. Set this parameter to NULL if the host did not use a delimiter + /// scripting engine. Set this parameter to null if the host did not use a delimiter /// to mark the end of the scriptlet. /// Application-defined value that is used for /// debugging purposes /// Zero-based value that specifies which line the /// parsing will begin at /// Flags associated with the scriptlet - /// The results of scriptlet processing, or NULL if the caller - /// expects no result (that is, the SCRIPTTEXT_ISEXPRESSION value is not set) - /// The exception information. This structure is filled - /// if IActiveScriptParse::ParseScriptText returns DISP_E_EXCEPTION. + /// The results of scriptlet processing, or null if the caller + /// expects no result (that is, the value is + /// not set) + /// The exception information. This structure is filled if + /// returns DISP_E_EXCEPTION. void ParseScriptText( [In] [MarshalAs(UnmanagedType.LPWStr)] string code, [In] [MarshalAs(UnmanagedType.LPWStr)] string itemName, [In] [MarshalAs(UnmanagedType.IUnknown)] object context, [In] [MarshalAs(UnmanagedType.LPWStr)] string delimiter, - [In] IntPtr pSourceContextCookie, + [In] UIntPtr pSourceContextCookie, [In] uint startingLineNumber, [In] ScriptTextFlags flags, [Out] out object result, - [Out] out EXCEPINFO exceptionInfo); + [Out] out EXCEPINFO exceptionInfo + ); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptParse64.cs b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptParse64.cs index 9eb9d78..bb98f86 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptParse64.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptParse64.cs @@ -1,26 +1,23 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - using System; - using System.Runtime.InteropServices; +#if NETFRAMEWORK +using System; +using System.Runtime.InteropServices; - using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; +using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; +namespace MsieJavaScriptEngine.ActiveScript +{ /// - /// If the Windows Script engine allows raw text code scriptlets to be added to the script + /// If the Active Script engine allows raw text code scriptlets to be added to the script /// or allows expression text to be evaluated at run time, it implements the - /// IActiveScriptParse interface. For interpreted scripting languages that have no - /// independent authoring environment, such as VBScript, this provides an alternate - /// mechanism (other than IPersist*) to get script code into the scripting engine, and - /// to attach script fragments to various object events. + /// interface. /// /// - /// Before the scripting engine can be used, one of the following methods must be called - /// : IPersist*::Load, IPersist*::InitNew, or IActiveScriptParse::InitNew. The semantics - /// of this method are identical to IPersistStreamInit::InitNew, in that this method tells - /// the scripting engine to initialize itself. Note that it is not valid to call both - /// IPersist*::InitNew or IActiveScriptParse::InitNew and IPersist*::Load, nor is it valid - /// to call IPersist*::InitNew, IActiveScriptParse::InitNew, or IPersist*::Load more - /// than once. + /// Before the scripting engine can be used, one of the following methods must be called: + /// IPersist.Load, IPersist.InitNew, or . The semantics of + /// this method are identical to IPersistStreamInit.InitNew, in that this method tells the scripting + /// engine to initialize itself. Note that it is not valid to call both IPersist.InitNew or + /// and IPersist.Load, nor is it valid to call + /// IPersist.InitNew, , or IPersist.Load more than once. /// [ComImport] [Guid("c7ef7658-e1ee-480e-97ea-d52cb4d76d17")] @@ -35,35 +32,36 @@ internal interface IActiveScriptParse64 /// /// Adds a code scriptlet to the script. This method is used in environments where the /// persistent state of the script is intertwined with the host document and the host - /// is responsible for restoring the script, rather than through an IPersist* interface. + /// is responsible for restoring the script, rather than through an IPersist interface. /// The primary examples are HTML scripting languages that allow scriptlets of code /// embedded in the HTML document to be attached to intrinsic events (for instance, - /// ONCLICK="button1.text='Exit'"). + /// ONCLICK="button1.text='Exit'"). /// /// The default name to associate with the scriptlet. If the - /// scriptlet does not contain naming information (as in the ONCLICK example above), - /// this name will be used to identify the scriptlet. If this parameter is NULL, the + /// scriptlet does not contain naming information (as in the ONCLICK example above), + /// this name will be used to identify the scriptlet. If this parameter is null, the /// scripting engine manufactures a unique name, if necessary. /// The scriptlet text to add. The interpretation of this string /// depends on the scripting language. /// The item name associated with this scriptlet. This parameter, - /// in addition to pstrSubItemName, identifies the object for which the scriptlet is - /// an event handler. + /// in addition to , identifies the object for which the + /// scriptlet is an event handler. /// The name of a subobject of the named item with which this /// scriptlet is associated; this name must be found in the named item's type - /// information. This parameter is NULL if the scriptlet is to be associated with the - /// named item instead of a subitem. This parameter, in addition to pstrItemName, - /// identifies the specific object for which the scriptlet is an event handler. + /// information. This parameter is null if the scriptlet is to be associated with the + /// named item instead of a subitem. This parameter, in addition to + /// , identifies the specific object for which the scriptlet + /// is an event handler. /// The name of the event for which the scriptlet is an event /// handler - /// The end-of-scriptlet delimiter. When the pstrCode parameter - /// is parsed from a stream of text, the host typically uses a delimiter, such as two - /// single quotation marks (''), to detect the end of the scriptlet. This parameter + /// The end-of-scriptlet delimiter. When the + /// parameter is parsed from a stream of text, the host typically uses a delimiter, such as + /// two single quotation marks (''), to detect the end of the scriptlet. This parameter /// specifies the delimiter that the host used, allowing the scripting engine to /// provide some conditional primitive preprocessing (for example, replacing a single /// quotation mark ['] with two single quotation marks for use as a delimiter). /// Exactly how (and if) the scripting engine makes use of this information depends - /// on the scripting engine. Set this parameter to NULL if the host did not use a + /// on the scripting engine. Set this parameter to null if the host did not use a /// delimiter to mark the end of the scriptlet. /// Application-defined value that is used for /// debugging purposes @@ -72,10 +70,10 @@ internal interface IActiveScriptParse64 /// Flags associated with the scriptlet /// Actual name used to identify the scriptlet. This is to be in /// order of preference: a name explicitly specified in the scriptlet text, the - /// default name provided in pstrDefaultName, or a unique name synthesized by the - /// scripting engine. + /// default name provided in , or a unique name + /// synthesized by the scripting engine. /// Exception information. This structure should be - /// filled in if DISP_E_EXCEPTION is returned + /// filled in if DISP_E_EXCEPTION is returned void AddScriptlet( [In] [MarshalAs(UnmanagedType.LPWStr)] string defaultName, [In] [MarshalAs(UnmanagedType.LPWStr)] string code, @@ -87,7 +85,8 @@ [In] [MarshalAs(UnmanagedType.LPWStr)] string delimiter, [In] uint startingLineNumber, [In] ScriptTextFlags flags, [Out] [MarshalAs(UnmanagedType.BStr)] out string name, - [Out] out EXCEPINFO exceptionInfo); + [Out] out EXCEPINFO exceptionInfo + ); /// /// Parses the given code scriptlet, adding declarations into the namespace and @@ -96,39 +95,42 @@ [Out] [MarshalAs(UnmanagedType.BStr)] out string name, /// The scriptlet text to evaluate. The interpretation of this /// string depends on the scripting language /// The item name that gives the context in which the - /// scriptlet is to be evaluated. If this parameter is NULL, the code is evaluated + /// scriptlet is to be evaluated. If this parameter is null, the code is evaluated /// in the scripting engine's global context /// The context object. This object is reserved for use in a /// debugging environment, where such a context may be provided by the debugger to - /// represent an active run-time context. If this parameter is NULL, the engine - /// uses pstrItemName to identify the context. - /// The end-of-scriptlet delimiter. When pstrCode is parsed - /// from a stream of text, the host typically uses a delimiter, such as two single - /// quotation marks (''), to detect the end of the scriptlet. This parameter specifies - /// the delimiter that the host used, allowing the scripting engine to provide some - /// conditional primitive preprocessing (for example, replacing a single quotation + /// represent an active run-time context. If this parameter is null, the engine + /// uses to identify the context. + /// The end-of-scriptlet delimiter. When + /// is parsed from a stream of text, the host typically uses a delimiter, such as two + /// single quotation marks (''), to detect the end of the scriptlet. This parameter + /// specifies the delimiter that the host used, allowing the scripting engine to provide + /// some conditional primitive preprocessing (for example, replacing a single quotation /// mark ['] with two single quotation marks for use as a delimiter). Exactly how /// (and if) the scripting engine makes use of this information depends on the - /// scripting engine. Set this parameter to NULL if the host did not use a delimiter + /// scripting engine. Set this parameter to null if the host did not use a delimiter /// to mark the end of the scriptlet. /// Application-defined value that is used for /// debugging purposes /// Zero-based value that specifies which line the /// parsing will begin at /// Flags associated with the scriptlet - /// The results of scriptlet processing, or NULL if the caller - /// expects no result (that is, the SCRIPTTEXT_ISEXPRESSION value is not set) - /// The exception information. This structure is filled - /// if IActiveScriptParse::ParseScriptText returns DISP_E_EXCEPTION. + /// The results of scriptlet processing, or null if the caller + /// expects no result (that is, the value is + /// not set) + /// The exception information. This structure is filled if + /// returns DISP_E_EXCEPTION. void ParseScriptText( [In] [MarshalAs(UnmanagedType.LPWStr)] string code, [In] [MarshalAs(UnmanagedType.LPWStr)] string itemName, [In] [MarshalAs(UnmanagedType.IUnknown)] object context, [In] [MarshalAs(UnmanagedType.LPWStr)] string delimiter, - [In] IntPtr pSourceContextCookie, + [In] UIntPtr pSourceContextCookie, [In] uint startingLineNumber, [In] ScriptTextFlags flags, [Out] out object result, - [Out] out EXCEPINFO exceptionInfo); + [Out] out EXCEPINFO exceptionInfo + ); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptParseWrapper.cs b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptParseWrapper.cs deleted file mode 100644 index af07d7b..0000000 --- a/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptParseWrapper.cs +++ /dev/null @@ -1,122 +0,0 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - using System; - using System.Runtime.InteropServices; - - /// - /// If the Windows Script engine allows raw text code scriptlets to be added to the script - /// or allows expression text to be evaluated at run time, it implements the - /// IActiveScriptParse interface. For interpreted scripting languages that have no - /// independent authoring environment, such as VBScript, this provides an alternate - /// mechanism (other than IPersist*) to get script code into the scripting engine, and - /// to attach script fragments to various object events. - /// - /// - /// Before the scripting engine can be used, one of the following methods must be called - /// : IPersist*::Load, IPersist*::InitNew, or IActiveScriptParse::InitNew. The semantics - /// of this method are identical to IPersistStreamInit::InitNew, in that this method tells - /// the scripting engine to initialize itself. Note that it is not valid to call both - /// IPersist*::InitNew or IActiveScriptParse::InitNew and IPersist*::Load, nor is it valid - /// to call IPersist*::InitNew, IActiveScriptParse::InitNew, or IPersist*::Load more - /// than once. - /// - [ComVisible(false)] - internal interface IActiveScriptParseWrapper : IDisposable - { - /// - /// Initializes the scripting engine. - /// - void InitNew(); - - /// - /// Adds a code scriptlet to the script. This method is used in environments where the - /// persistent state of the script is intertwined with the host document and the host - /// is responsible for restoring the script, rather than through an IPersist* interface. - /// The primary examples are HTML scripting languages that allow scriptlets of code - /// embedded in the HTML document to be attached to intrinsic events (for instance, - /// ONCLICK="button1.text='Exit'"). - /// - /// The default name to associate with the scriptlet. If the - /// scriptlet does not contain naming information (as in the ONCLICK example above), - /// this name will be used to identify the scriptlet. If this parameter is NULL, the - /// scripting engine manufactures a unique name, if necessary. - /// The scriptlet text to add. The interpretation of this string - /// depends on the scripting language - /// The item name associated with this scriptlet. This parameter, - /// in addition to pstrSubItemName, identifies the object for which the scriptlet is - /// an event handler - /// The name of a subobject of the named item with which this - /// scriptlet is associated; this name must be found in the named item's type - /// information. This parameter is NULL if the scriptlet is to be associated with the - /// named item instead of a subitem. This parameter, in addition to pstrItemName, - /// identifies the specific object for which the scriptlet is an event handler. - /// The name of the event for which the scriptlet is an event - /// handler - /// The end-of-scriptlet delimiter. When the pstrCode parameter - /// is parsed from a stream of text, the host typically uses a delimiter, such as two - /// single quotation marks (''), to detect the end of the scriptlet. This parameter - /// specifies the delimiter that the host used, allowing the scripting engine to - /// provide some conditional primitive preprocessing (for example, replacing a single - /// quotation mark ['] with two single quotation marks for use as a delimiter). - /// Exactly how (and if) the scripting engine makes use of this information depends - /// on the scripting engine. Set this parameter to NULL if the host did not use a - /// delimiter to mark the end of the scriptlet. - /// Application-defined value that is used for - /// debugging purposes - /// Zero-based value that specifies which line the - /// parsing will begin at - /// Flags associated with the scriptlet. - /// Actual name used to identify the scriptlet. This is to be in - /// order of preference: a name explicitly specified in the scriptlet text, the - /// default name provided in pstrDefaultName, or a unique name synthesized by the - /// scripting engine - string AddScriptlet( - string defaultName, - string code, - string itemName, - string subItemName, - string eventName, - string delimiter, - IntPtr sourceContextCookie, - uint startingLineNumber, - ScriptTextFlags flags); - - /// - /// Parses the given code scriptlet, adding declarations into the namespace and - /// evaluating code as appropriate - /// - /// The scriptlet text to evaluate. The interpretation of this - /// string depends on the scripting language - /// The item name that gives the context in which the - /// scriptlet is to be evaluated. If this parameter is NULL, the code is evaluated - /// in the scripting engine's global context. - /// The context object. This object is reserved for use in a - /// debugging environment, where such a context may be provided by the debugger to - /// represent an active run-time context. If this parameter is NULL, the engine - /// uses pstrItemName to identify the context - /// The end-of-scriptlet delimiter. When pstrCode is parsed - /// from a stream of text, the host typically uses a delimiter, such as two single - /// quotation marks (''), to detect the end of the scriptlet. This parameter specifies - /// the delimiter that the host used, allowing the scripting engine to provide some - /// conditional primitive preprocessing (for example, replacing a single quotation - /// mark ['] with two single quotation marks for use as a delimiter). Exactly how - /// (and if) the scripting engine makes use of this information depends on the - /// scripting engine. Set this parameter to NULL if the host did not use a delimiter - /// to mark the end of the scriptlet. - /// Application-defined value that is used for - /// debugging purposes - /// Zero-based value that specifies which line the - /// parsing will begin at - /// Flags associated with the scriptlet - /// The results of scriptlet processing, or NULL if the caller - /// expects no result (that is, the SCRIPTTEXT_ISEXPRESSION value is not set) - object ParseScriptText( - string code, - string itemName, - object context, - string delimiter, - IntPtr sourceContextCookie, - uint startingLineNumber, - ScriptTextFlags flags); - } -} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptProperty.cs b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptProperty.cs index f08db94..0cc4785 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptProperty.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptProperty.cs @@ -1,23 +1,45 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - using System; - using System.Runtime.InteropServices; +#if NETFRAMEWORK +using System; +using System.Runtime.InteropServices; +namespace MsieJavaScriptEngine.ActiveScript +{ + /// + /// The interface is used to get and set + /// configuration properties + /// [ComImport] [Guid("4954e0d0-fbc7-11d1-8410-006008c3fbfc")] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] internal interface IActiveScriptProperty { + /// + /// Gets the property that is specified by the parameter + /// + /// The property value to get + /// Not used + /// The value of the property + /// The method returns an HRESULT [PreserveSig] uint GetProperty( [In] uint dwProperty, [In] IntPtr pvarIndex, - [Out] out object pvarValue); + [Out] out object pvarValue + ); + /// + /// Sets the property that is specified by the parameter + /// + /// The property value to set + /// Not used + /// The value of the property + /// The method returns an HRESULT [PreserveSig] uint SetProperty( [In] uint dwProperty, [In] IntPtr pvarIndex, - [In] [Out] ref object pvarValue); + [In] [Out] ref object pvarValue + ); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptSite.cs b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptSite.cs index 65301ea..494dff0 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptSite.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptSite.cs @@ -1,15 +1,17 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - using System; - using System.Runtime.InteropServices; +#if NETFRAMEWORK +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; - using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; +using EXCEPINFO = System.Runtime.InteropServices.ComTypes.EXCEPINFO; +namespace MsieJavaScriptEngine.ActiveScript +{ /// - /// Implemented by the host to create a site for the Windows Script engine. Usually, this site - /// will be associated with the container of all the objects that are visible to the script - /// (for example, the ActiveX Controls). Typically, this container will correspond to the document - /// or page being viewed. Microsoft Internet Explorer, for example, would create such a container + /// Implemented by the host to create a site for the Active Script engine. Usually, this site + /// will be associated with the container of all the objects that are visible to the script (for + /// example, the ActiveX Controls). Typically, this container will correspond to the document or + /// page being viewed. Microsoft Internet Explorer, for example, would create such a container /// for each HTML page being displayed. Each ActiveX control (or other automation object) on the /// page, and the scripting engine itself, would be enumerable within this container. /// @@ -26,84 +28,92 @@ internal interface IActiveScriptSite /// A variable that receives the locale identifier for user-interface /// elements displayed by the scripting engine void GetLcid( - [Out] out int lcid); + [Out] out int lcid + ); /// /// Allows the scripting engine to obtain information about an item added with the - /// IActiveScript.AddNamedItem method + /// method /// /// The name associated with the item, as specified in the - /// IActiveScript.AddNamedItem method + /// method /// A bit mask specifying what information about the item should be /// returned. The scripting engine should request the minimum amount of information possible - /// because some of the return parameters (for example, ITypeInfo) can take considerable - /// time to load or generate. - /// A variable that receives a pointer to the IUnknown interface associated - /// with the given item. The scripting engine can use the IUnknown.QueryInterface method to - /// obtain the IDispatch interface for the item. This parameter receives null if mask - /// does not include the ScriptInfo.IUnknown value. Also, it receives null if there is no - /// object associated with the item name; this mechanism is used to create a simple class when - /// the named item was added with the ScriptItem.CodeOnly flag set in the - /// IActiveScript.AddNamedItem method. - /// A variable that receives a pointer to the ITypeInfo interface - /// associated with the item. This parameter receives null if mask does not include the - /// ScriptInfo.ITypeInfo value, or if type information is not available for this item. If type - /// information is not available, the object cannot source events, and name binding must be - /// realized with the IDispatch.GetIDsOfNames method. Note that the ITypeInfo interface - /// retrieved describes the item's coclass (TKIND_COCLASS) because the object may support - /// multiple interfaces and event interfaces. If the item supports the IProvideMultipleTypeInfo - /// interface, the ITypeInfo interface retrieved is the same as the index zero ITypeInfo that - /// would be obtained using the IProvideMultipleTypeInfo.GetInfoOfIndex method. + /// because some of the return parameters (for example, ) can take + /// considerable time to load or generate. + /// A variable that receives a pointer to the IUnknown interface + /// associated with the given item. The scripting engine can use the IUnknown.QueryInterface + /// method to obtain the IDispatch interface for the item. This parameter receives null if + /// mask does not include the value. Also, it receives + /// null if there is no object associated with the item name; this mechanism is used to create + /// a simple class when the named item was added with the + /// flag set in the method. + /// A variable that receives a pointer to the + /// interface associated with the item. This parameter receives null if mask does not include + /// the value, or if type information is not available + /// for this item. If type information is not available, the object cannot source events, and + /// name binding must be realized with the IDispatch.GetIDsOfNames method. Note that the + /// interface retrieved describes the item's coclass (TKIND_COCLASS) + /// because the object may support multiple interfaces and event interfaces. If the item supports + /// the IProvideMultipleTypeInfo interface, the interface retrieved is + /// the same as the index zero that would be obtained using the + /// IProvideMultipleTypeInfo.GetInfoOfIndex method. void GetItemInfo( [In] [MarshalAs(UnmanagedType.LPWStr)] string name, [In] ScriptInfoFlags mask, [In] [Out] ref IntPtr pUnkItem, - [In] [Out] ref IntPtr pTypeInfo); + [In] [Out] ref IntPtr pTypeInfo + ); /// /// Retrieves a host-defined string that uniquely identifies the current document version. If - /// the related document has changed outside the scope of Windows Script (as in the case of an + /// the related document has changed outside the scope of Active Script (as in the case of an /// HTML page being edited with Notepad), the scripting engine can save this along with its /// persisted state, forcing a recompile the next time the script is loaded. /// /// The host-defined document version string void GetDocVersionString( - [Out] [MarshalAs(UnmanagedType.BStr)] out string version); + [Out] [MarshalAs(UnmanagedType.BStr)] out string version + ); /// - /// Informs the host that the script has completed execution. + /// Informs the host that the script has completed execution /// - /// A variable that contains the script result, or null if the script - /// produced no result. + /// A variable that contains the script result, or null if the script + /// produced no result /// Contains exception information generated when the script - /// terminated, or null if no exception was generated + /// terminated, or null if no exception was generated void OnScriptTerminate( [In] object result, - [In] EXCEPINFO exceptionInfo); + [In] EXCEPINFO exceptionInfo + ); /// - /// Informs the host that the scripting engine has changed states. + /// Informs the host that the scripting engine has changed states /// - /// Indicates the new script state. + /// Indicates the new script state void OnStateChange( - [In] ScriptState state); + [In] ScriptState state + ); /// - /// Informs the host that an execution error occurred while the engine was running the script. + /// Informs the host that an execution error occurred while the engine was running the script /// /// A host can use this interface to obtain information about the - /// execution error. + /// execution error void OnScriptError( - [In] IActiveScriptError error); + [In] IActiveScriptError error + ); /// - /// Informs the host that the scripting engine has begun executing the script code. + /// Informs the host that the scripting engine has begun executing the script code /// void OnEnterScript(); /// - /// Informs the host that the scripting engine has returned from executing script code. + /// Informs the host that the scripting engine has returned from executing script code /// void OnLeaveScript(); } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptSiteInterruptPoll.cs b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptSiteInterruptPoll.cs new file mode 100644 index 0000000..42aeb21 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptSiteInterruptPoll.cs @@ -0,0 +1,23 @@ +#if NETFRAMEWORK +using System.Runtime.InteropServices; + +namespace MsieJavaScriptEngine.ActiveScript +{ + /// + /// The interface allows a host to specify + /// that ascript should terminate + /// + [ComImport] + [Guid("539698a0-cdca-11cf-a5eb-00aa0047a063")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + internal interface IActiveScriptSiteInterruptPoll + { + /// + /// Allows a host to specify that a script should terminate + /// + /// The method returns an HRESULT + [PreserveSig] + uint QueryContinue(); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptWrapper.cs b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptWrapper.cs new file mode 100644 index 0000000..fb1bcd1 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/IActiveScriptWrapper.cs @@ -0,0 +1,139 @@ +#if NETFRAMEWORK +using System; +using System.Runtime.InteropServices.ComTypes; + +using MsieJavaScriptEngine.ActiveScript.Debugging; + +namespace MsieJavaScriptEngine.ActiveScript +{ + internal interface IActiveScriptWrapper : IDisposable + { + /// + /// Informs the scripting engine of the interface site + /// provided by the host. Call this method before any other + /// interface methods is used. + /// + /// The host-supplied script site to be associated with this instance + /// of the scripting engine. The site must be uniquely assigned to this scripting engine + /// instance; it cannot be shared with other scripting engines. + void SetScriptSite( + IActiveScriptSite site + ); + + /// + /// Puts the scripting engine into the given state. This method can be called from non-base + /// threads without resulting in a non-base callout to host objects or to the + /// interface. + /// + /// Sets the scripting engine to the given state + void SetScriptState( + ScriptState state + ); + + /// + /// Adds the name of a root-level item to the scripting engine's name space. A root-level item + /// is an object with properties and methods, an event source, or all three. + /// + /// The name of the item as viewed from the script. The name must be unique + /// and persistable + /// Flags associated with an item + void AddNamedItem( + string name, + ScriptItemFlags flags + ); + + /// + /// Gets a script dispatch + /// + /// The object associated with the script's global methods and properties + object GetScriptDispatch(); + + /// + /// Initializes the scripting engine + /// + void InitNew(); + + /// + /// Parses the given code scriptlet, adding declarations into the namespace and + /// evaluating code as appropriate + /// + /// The scriptlet text to evaluate. The interpretation of this + /// string depends on the scripting language + /// The item name that gives the context in which the + /// scriptlet is to be evaluated. If this parameter is null, the code is evaluated + /// in the scripting engine's global context + /// The context object. This object is reserved for use in a + /// debugging environment, where such a context may be provided by the debugger to + /// represent an active run-time context. If this parameter is null, the engine + /// uses to identify the context. + /// The end-of-scriptlet delimiter. When + /// is parsed from a stream of text, the host typically uses a delimiter, such as two + /// single quotation marks (''), to detect the end of the scriptlet. This parameter + /// specifies the delimiter that the host used, allowing the scripting engine to provide + /// some conditional primitive preprocessing (for example, replacing a single quotation + /// mark ['] with two single quotation marks for use as a delimiter). Exactly how + /// (and if) the scripting engine makes use of this information depends on the + /// scripting engine. Set this parameter to null if the host did not use a delimiter + /// to mark the end of the scriptlet. + /// Application-defined value that is used for + /// debugging purposes + /// Zero-based value that specifies which line the + /// parsing will begin at + /// Flags associated with the scriptlet + /// The results of scriptlet processing, or null if the caller expects no + /// result (that is, the value is not set) + object ParseScriptText( + string code, + string itemName, + object context, + string delimiter, + UIntPtr sourceContextCookie, + uint startingLineNumber, + ScriptTextFlags flags + ); + + /// + /// Used by a smart host to delegate the method + /// + /// The source context as provided to + /// or + /// + /// Character offset relative to start of script text + /// Number of characters in this context + /// An enumerator of the code contexts in the specified range + void EnumCodeContextsOfPosition( + UIntPtr sourceContext, + uint offset, + uint length, + out IEnumDebugCodeContexts enumContexts + ); + + void EnumStackFrames( + out IEnumDebugStackFrames enumFrames + ); + + /// + /// Interrupts the execution of a running script thread (an event sink, an immediate execution, + /// or a macro invocation). This method can be used to terminate a script that is stuck (for + /// example, in an infinite loop). It can be called from non-base threads without resulting in + /// a non-base callout to host objects or to the method. + /// + /// Identifier of the thread to interrupt + /// The error information that should be reported to the aborted script + /// Option flags associated with the interruption + void InterruptScriptThread( + uint scriptThreadId, + ref EXCEPINFO exceptionInfo, + ScriptInterruptFlags flags + ); + + /// + /// The Active Script host calls this method to start garbage collection + /// + /// The type of garbage collection + void CollectGarbage( + ScriptGCType type + ); + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/JScriptRuntimeErrorNumber.cs b/src/MsieJavaScriptEngine/ActiveScript/JScriptRuntimeErrorNumber.cs new file mode 100644 index 0000000..fd135a0 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/JScriptRuntimeErrorNumber.cs @@ -0,0 +1,183 @@ +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.ActiveScript +{ + /// + /// JScript runtime error numbers + /// + internal static class JScriptRuntimeErrorNumber + { + #region Engine + + /// + /// Out of memory + /// + public const int OutOfMemory = 7; + + #endregion + + #region Runtime + + /// + /// Out of stack space + /// + public const int OutOfStackSpace = 28; + + /// + /// Cannot assign to this + /// + public const int CannotAssignToThisKeyword = 5000; + + /// + /// Number expected + /// + public const int NumberExpected = 5001; + + /// + /// Function expected + /// + public const int FunctionExpected = 5002; + + /// + /// Cannot assign to a function result + /// + public const int CannotAssignToFunctionResult = 5003; + + /// + /// String expected + /// + public const int StringExpected = 5005; + + /// + /// Date object expected + /// + public const int DateObjectExpected = 5006; + + /// + /// Object expected + /// + public const int ObjectExpected = 5007; + + /// + /// Illegal assignment + /// + public const int IllegalAssignment = 5008; + + /// + /// Undefined identifier + /// + public const int UndefinedIdentifier = 5009; + + /// + /// Boolean expected + /// + public const int BooleanExpected = 5010; + + /// + /// Object member expected + /// + public const int ObjectMemberExpected = 5012; + + /// + /// VBArray expected + /// + public const int VbArrayExpected = 5013; + + /// + /// JavaScript object expected + /// + public const int JavaScriptObjectExpected = 5014; + + /// + /// Enumerator object expected + /// + public const int EnumeratorObjectExpected = 5015; + + /// + /// Regular Expression object expected + /// + public const int RegularExpressionObjectExpected = 5016; + + /// + /// Syntax error in regular expression + /// + public const int SyntaxErrorInRegularExpression = 5017; + + /// + /// Unexpected quantifier + /// + public const int UnexpectedQuantifier = 5018; + + /// + /// Expected ] in regular expression + /// + public const int ExpectedRightSquareBracketInRegularExpression = 5019; + + /// + /// Expected ) in regular expression + /// + public const int ExpectedRightParenthesisInRegularExpression = 5020; + + /// + /// Invalid range in character set + /// + public const int InvalidRangeInCharacterSet = 5021; + + /// + /// Exception thrown and not caught + /// + public const int ExceptionThrownAndNotCaught = 5022; + + /// + /// Function does not have a valid prototype object + /// + public const int FunctionDoesNotHaveValidPrototypeObject = 5023; + + /// + /// The URI to be encoded contains an invalid character + /// + public const int UriToBeEncodedContainsInvalidCharacter = 5024; + + /// + /// The URI to be decoded is not a valid encoding + /// + public const int UriToBeDecodedIsNotValidEncoding = 5025; + + /// + /// The number of fractional digits is out of range + /// + public const int NumberOfFractionalDigitsIsOutOfRange = 5026; + + /// + /// The precision is out of range + /// + public const int PrecisionOutOfRange = 5027; + + /// + /// Array or arguments object expected + /// + public const int ArrayOrArgumentsObjectExpected = 5028; + + /// + /// Array length must be a finite positive integer + /// + public const int ArrayLengthMustBeFinitePositiveInteger = 5029; + + /// + /// Array length must be assigned a finite positive number + /// + public const int ArrayLengthMustBeAssignedFinitePositiveNumber = 5030; + + /// + /// Circular reference in value argument not supported + /// + public const int CircularReferenceInValueArgumentNotSupported = 5034; + + /// + /// Invalid replacer argument + /// + public const int InvalidReplacerArgument = 5035; + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/JScriptSyntaxErrorNumber.cs b/src/MsieJavaScriptEngine/ActiveScript/JScriptSyntaxErrorNumber.cs new file mode 100644 index 0000000..76544e7 --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/JScriptSyntaxErrorNumber.cs @@ -0,0 +1,168 @@ +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.ActiveScript +{ + /// + /// JScript syntax error numbers + /// + internal static class JScriptSyntaxErrorNumber + { + #region Engine + + /// + /// Out of memory + /// + public const int OutOfMemory = 1001; + + #endregion + + #region Compilation + + /// + /// Syntax error + /// + public const int SyntaxError = 1002; + + /// + /// Expected : + /// + public const int ExpectedColon = 1003; + + /// + /// Expected ; + /// + public const int ExpectedSemicolon = 1004; + + /// + /// Expected ( + /// + public const int ExpectedLeftParenthesis = 1005; + + /// + /// Expected ) + /// + public const int ExpectedRightParenthesis = 1006; + + /// + /// Expected ] + /// + public const int ExpectedRightSquareBracket = 1007; + + /// + /// Expected { + /// + public const int ExpectedLeftCurlyBrace = 1008; + + /// + /// Expected } + /// + public const int ExpectedRightCurlyBrace = 1009; + + /// + /// Expected identifier + /// + public const int ExpectedIdentifier = 1010; + + /// + /// Expected = + /// + public const int ExpectedEqualSign = 1011; + + /// + /// Expected / + /// + public const int ExpectedForwardSlash = 1012; + + /// + /// Invalid character + /// + public const int InvalidCharacter = 1014; + + /// + /// Unterminated string constant + /// + public const int UnterminatedStringConstant = 1015; + + /// + /// Unterminated comment + /// + public const int UnterminatedComment = 1016; + + /// + /// return statement outside of function + /// + public const int ReturnStatementOutsideOfFunction = 1018; + + /// + /// Can't have break outside of loop + /// + public const int CannotHaveBreakStatementOutsideOfLoop = 1019; + + /// + /// Can't have continue outside of loop + /// + public const int CannotHaveContinueStatementOutsideOfLoop = 1020; + + /// + /// Expected hexadecimal digit + /// + public const int ExpectedHexadecimalDigit = 1023; + + /// + /// Expected while + /// + public const int ExpectedWhileStatement = 1024; + + /// + /// Label redefined + /// + public const int LabelRedefined = 1025; + + /// + /// Label not found + /// + public const int LabelNotFound = 1026; + + /// + /// default can only appear once in a switch statement + /// + public const int DefaultStatementCanOnlyAppearOnceInSwitchStatement = 1027; + + /// + /// Expected identifier, string or number + /// + public const int ExpectedIdentifierStringOrNumber = 1028; + + /// + /// Expected @end + /// + public const int ExpectedConditionalCompilationEndStatement = 1029; + + /// + /// Conditional compilation is turned off + /// + public const int ConditionalCompilationTurnedOff = 1030; + + /// + /// Expected constant + /// + public const int ExpectedConstant = 1031; + + /// + /// Expected @ + /// + public const int ExpectedAtSign = 1032; + + /// + /// Expected catch + /// + public const int ExpectedCatchStatement = 1033; + + /// + /// Throw must be followed by an expression on the same source line + /// + public const int ThrowMustBeFollowedByExpressionOnSameSourceLine = 1035; + + #endregion + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ScriptGCType.cs b/src/MsieJavaScriptEngine/ActiveScript/ScriptGCType.cs new file mode 100644 index 0000000..49f816a --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/ScriptGCType.cs @@ -0,0 +1,20 @@ +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.ActiveScript +{ + /// + /// The type of garbage collection to perform + /// + internal enum ScriptGCType + { + /// + /// Do normal garbage collection + /// + Normal = 0, + + /// + /// Do exhaustive garbage collection + /// + Exhaustive = 1 + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ScriptHResult.cs b/src/MsieJavaScriptEngine/ActiveScript/ScriptHResult.cs deleted file mode 100644 index 999276c..0000000 --- a/src/MsieJavaScriptEngine/ActiveScript/ScriptHResult.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - /// - /// Common HRESULT Values - /// - internal enum ScriptHResult : uint - { - /// - /// Success - /// - Ok = 0x00000000, - - /// - /// An argument is not valid - /// - InvalidArg = 0x80070057, - - /// - /// The call was not expected (for example, the scripting engine - /// has not yet been loaded or initialized) - /// - Unexpected = 0x8000FFFF - } -} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ScriptInfoFlags.cs b/src/MsieJavaScriptEngine/ActiveScript/ScriptInfoFlags.cs index eea4e89..26088b3 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/ScriptInfoFlags.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/ScriptInfoFlags.cs @@ -1,7 +1,8 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - using System; +#if NETFRAMEWORK +using System; +namespace MsieJavaScriptEngine.ActiveScript +{ [Flags] internal enum ScriptInfoFlags : uint { @@ -11,13 +12,14 @@ internal enum ScriptInfoFlags : uint None = 0, /// - /// Returns the IUnknown interface for this item + /// Returns the IUnknown interface for this item /// IUnknown = 1, /// - /// Returns the ITypeInfo interface for this item + /// Returns the ITypeInfo interface for this item /// ITypeInfo = 2 } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ScriptInterruptFlags.cs b/src/MsieJavaScriptEngine/ActiveScript/ScriptInterruptFlags.cs index 1d731e7..04d4bb9 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/ScriptInterruptFlags.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/ScriptInterruptFlags.cs @@ -1,7 +1,8 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - using System; +#if NETFRAMEWORK +using System; +namespace MsieJavaScriptEngine.ActiveScript +{ /// /// Thread interruption options /// @@ -23,6 +24,7 @@ internal enum ScriptInterruptFlags : uint /// Otherwise, the script method is aborted and the error code is returned to the caller; that /// is, the event source or macro invoker. /// - RaiseException = 2, + RaiseException = 2 } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ScriptItemFlags.cs b/src/MsieJavaScriptEngine/ActiveScript/ScriptItemFlags.cs index 54de65e..b627cae 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/ScriptItemFlags.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/ScriptItemFlags.cs @@ -1,7 +1,8 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - using System; +#if NETFRAMEWORK +using System; +namespace MsieJavaScriptEngine.ActiveScript +{ [Flags] internal enum ScriptItemFlags : uint { @@ -29,11 +30,11 @@ internal enum ScriptItemFlags : uint /// /// Indicates that the item is a collection of global properties and methods associated /// with the script. Normally, a scripting engine would ignore the object name (other than - /// for the purpose of using it as a cookie for the IActiveScriptSite.GetItemInfo method, + /// for the purpose of using it as a cookie for the method, /// or for resolving explicit scoping) and expose its members as global variables and /// methods. This allows the host to extend the library (run-time functions and so on) /// available to the script. It is left to the scripting engine to deal with name conflicts - /// (for example, when two ScriptItemFlags.GlobalMembers items have methods of the same + /// (for example, when two items have methods of the same /// name), although an error should not be returned because of this situation. /// GlobalMembers = 0x00000008, @@ -48,7 +49,7 @@ internal enum ScriptItemFlags : uint /// /// Indicates that the named item represents a code-only object, and that the host has no - /// IUnknown to be associated with this code-only object. The host only has a name for this + /// IUnknown to be associated with this code-only object. The host only has a name for this /// object. In object-oriented languages such as C++, this flag would create a class. /// Not all languages support this flag. /// @@ -60,6 +61,7 @@ internal enum ScriptItemFlags : uint /// without this flag being set, VBScript will create a separate module for the named item, /// and C++ might create a separate wrapper class for the named item. /// - NoCode = 0x00000400, + NoCode = 0x00000400 } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ScriptLanguageVersion.cs b/src/MsieJavaScriptEngine/ActiveScript/ScriptLanguageVersion.cs index 67e5e4e..d4c90ca 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/ScriptLanguageVersion.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/ScriptLanguageVersion.cs @@ -1,7 +1,8 @@ -namespace MsieJavaScriptEngine.ActiveScript +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.ActiveScript { /// - /// Version of script language (see https://msdn.microsoft.com/en-gb/library/hh769820(v=vs.94).aspx) + /// Specifies the possible scripting versions /// internal enum ScriptLanguageVersion { @@ -28,6 +29,7 @@ internal enum ScriptLanguageVersion /// /// ECMAScript 5 /// - EcmaScript5 = 3, + EcmaScript5 = 3 } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ScriptProperty.cs b/src/MsieJavaScriptEngine/ActiveScript/ScriptProperty.cs index 7f5c1e1..6d83292 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/ScriptProperty.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/ScriptProperty.cs @@ -1,4 +1,5 @@ -namespace MsieJavaScriptEngine.ActiveScript +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.ActiveScript { /// /// Script property (see https://msdn.microsoft.com/en-us/subscriptions/downloads/cc512774(v=vs.94).aspx) @@ -7,7 +8,7 @@ internal enum ScriptProperty : uint { /// /// Forces the scripting engine to divide in integer mode instead of floating point mode. - /// The default value is False. + /// The default value is false. /// IntegerMode = 0x00003000, @@ -29,4 +30,5 @@ internal enum ScriptProperty : uint /// InvokeVersioning = 0x00004000 } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ScriptState.cs b/src/MsieJavaScriptEngine/ActiveScript/ScriptState.cs index 3d87167..a8b8a92 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/ScriptState.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/ScriptState.cs @@ -1,44 +1,46 @@ -namespace MsieJavaScriptEngine.ActiveScript +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.ActiveScript { /// - /// Contains named constant values that specify the state of a scripting engine + /// Specifies the state of a scripting engine /// internal enum ScriptState : uint { /// - /// Script has just been created, but has not yet been initialized using an IPersist* - /// interface and IActiveScript.SetScriptSite + /// Script has just been created, but has not yet been initialized using an IPersist* + /// interface and /// Uninitialized = 0, + /// + /// Script has been initialized, but is not running (connecting to other objects or + /// sinking events) or executing any code. Code can be queried for execution by + /// calling the IActiveScriptParse.ParseScriptText method. + /// + Initialized = 1, + /// /// Script can execute code, but is not yet sinking the events of objects added by - /// the IActiveScript.AddNamedItem method + /// the IActiveScript.AddNamedItem method /// - Started = 1, + Started = 2, /// /// Script is loaded and connected for sinking events /// - Connected = 2, + Connected = 3, /// /// Script is loaded and has a run-time execution state, but is temporarily /// disconnected from sinking events /// - Disconnected = 3, + Disconnected = 4, /// /// Script has been closed. The scripting engine no longer works and returns errors /// for most methods /// - Closed = 4, - - /// - /// Script has been initialized, but is not running (connecting to other objects or - /// sinking events) or executing any code. Code can be queried for execution by - /// calling the IActiveScriptParse.ParseScriptText method. - /// - Initialized = 5 + Closed = 5 } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ScriptTextFlags.cs b/src/MsieJavaScriptEngine/ActiveScript/ScriptTextFlags.cs index 050f772..d8c9ba9 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/ScriptTextFlags.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/ScriptTextFlags.cs @@ -1,7 +1,8 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - using System; +#if NETFRAMEWORK +using System; +namespace MsieJavaScriptEngine.ActiveScript +{ [Flags] internal enum ScriptTextFlags : uint { @@ -24,12 +25,15 @@ internal enum ScriptTextFlags : uint /// /// Indicates that the code added during this call should be saved if the scripting engine is saved - /// (for example, through a call to IPersist*::Save), or if the scripting engine is reset by way of + /// (for example, through a call to IPersist*.Save), or if the scripting engine is reset by way of /// a transition back to the initialized state. For more information about this state, see Script /// Engine States /// IsPersistent = 0x00000040, + HostManagesSource = 0x00000080, - IsCrossDomain = 0x00000100, + + IsCrossDomain = 0x00000100 } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ScriptThreadId.cs b/src/MsieJavaScriptEngine/ActiveScript/ScriptThreadId.cs new file mode 100644 index 0000000..541f3ff --- /dev/null +++ b/src/MsieJavaScriptEngine/ActiveScript/ScriptThreadId.cs @@ -0,0 +1,25 @@ +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.ActiveScript +{ + /// + /// Used to specify the type of thread + /// + internal static class ScriptThreadId + { + /// + /// The currently executing thread + /// + public const uint Current = 0xFFFFFFFD; + + /// + /// The base thread; that is, the thread in which the scripting engine was instantiated + /// + public const uint Base = 0xFFFFFFFE; + + /// + /// All threads + /// + public const uint All = 0xFFFFFFFF; + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ScriptThreadState.cs b/src/MsieJavaScriptEngine/ActiveScript/ScriptThreadState.cs index 35fbcbc..bcafbb9 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/ScriptThreadState.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/ScriptThreadState.cs @@ -1,8 +1,8 @@ -namespace MsieJavaScriptEngine.ActiveScript +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.ActiveScript { /// - /// Contains named constant values that specify the state of a thread in a scripting - /// engine. This enumeration is used by the IActiveScript::GetScriptThreadState method + /// Specifies the state of a thread in a scripting engine /// internal enum ScriptThreadState : uint { @@ -16,6 +16,7 @@ internal enum ScriptThreadState : uint /// Specified thread is actively servicing a scripted event, processing /// immediately executed script text, or running a script macro /// - Running = 1, + Running = 1 } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/ActiveScript/ScriptTypeLibFlags.cs b/src/MsieJavaScriptEngine/ActiveScript/ScriptTypeLibFlags.cs index ce92732..ba20786 100644 --- a/src/MsieJavaScriptEngine/ActiveScript/ScriptTypeLibFlags.cs +++ b/src/MsieJavaScriptEngine/ActiveScript/ScriptTypeLibFlags.cs @@ -1,7 +1,8 @@ -namespace MsieJavaScriptEngine.ActiveScript -{ - using System; +#if NETFRAMEWORK +using System; +namespace MsieJavaScriptEngine.ActiveScript +{ [Flags] internal enum ScriptTypeLibFlags : uint { @@ -18,6 +19,7 @@ internal enum ScriptTypeLibFlags : uint /// /// Not documented /// - IsPersistent = 0x00000040, + IsPersistent = 0x00000040 } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Constants/ClassId.cs b/src/MsieJavaScriptEngine/Constants/ClassId.cs new file mode 100644 index 0000000..715c435 --- /dev/null +++ b/src/MsieJavaScriptEngine/Constants/ClassId.cs @@ -0,0 +1,14 @@ +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.Constants +{ + /// + /// Class identifiers + /// + internal static class ClassId + { + public const string Classic = "{f414c260-6ac0-11cf-b6d1-00aa00bbbb58}"; + + public const string Chakra = "{16d51579-a30b-4c8b-a276-0ff4dc41e755}"; + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Constants/ComErrorCode.cs b/src/MsieJavaScriptEngine/Constants/ComErrorCode.cs index 1da41b0..26b1222 100644 --- a/src/MsieJavaScriptEngine/Constants/ComErrorCode.cs +++ b/src/MsieJavaScriptEngine/Constants/ComErrorCode.cs @@ -1,12 +1,33 @@ -namespace MsieJavaScriptEngine.Constants +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.Constants { /// /// COM error codes /// internal static class ComErrorCode { - public const int ElementNotFound = unchecked((int)(0x8002802B)); + // ReSharper disable InconsistentNaming + public const int SEVERITY_SUCCESS = 0; + public const int SEVERITY_ERROR = 1; - public const int ClassNotRegistered = unchecked((int)(0x80040154)); + public const int FACILITY_NULL = 0; + public const int FACILITY_RPC = 1; + public const int FACILITY_DISPATCH = 2; + public const int FACILITY_STORAGE = 3; + public const int FACILITY_ITF = 4; + public const int FACILITY_WIN32 = 7; + public const int FACILITY_WINDOWS = 8; + public const int FACILITY_CONTROL = 10; + public const int FACILITY_INTERNET = 12; + public const int FACILITY_URT = 19; + + public const int S_OK = 0; + public const int S_FALSE = 1; + + public const int E_ABORT = unchecked((int)0x80004004); + public const int E_ELEMENT_NOT_FOUND = unchecked((int)0x8002802B); + public const int E_CLASS_NOT_REGISTERED = unchecked((int)0x80040154); + // ReSharper restore InconsistentNaming } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Constants/DllName.cs b/src/MsieJavaScriptEngine/Constants/DllName.cs index 3aca275..74bf627 100644 --- a/src/MsieJavaScriptEngine/Constants/DllName.cs +++ b/src/MsieJavaScriptEngine/Constants/DllName.cs @@ -5,6 +5,8 @@ /// internal static class DllName { + public const string JScript = "jscript.dll"; + public const string JScript9 = "jscript9.dll"; public const string Chakra = "chakra.dll"; diff --git a/src/MsieJavaScriptEngine/Constants/JsErrorCategory.cs b/src/MsieJavaScriptEngine/Constants/JsErrorCategory.cs new file mode 100644 index 0000000..58f6208 --- /dev/null +++ b/src/MsieJavaScriptEngine/Constants/JsErrorCategory.cs @@ -0,0 +1,14 @@ +namespace MsieJavaScriptEngine.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 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/MsieJavaScriptEngine/Constants/JsErrorType.cs b/src/MsieJavaScriptEngine/Constants/JsErrorType.cs new file mode 100644 index 0000000..7c90417 --- /dev/null +++ b/src/MsieJavaScriptEngine/Constants/JsErrorType.cs @@ -0,0 +1,15 @@ +namespace MsieJavaScriptEngine.Constants +{ + internal 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/MsieJavaScriptEngine/Constants/SpecialMemberName.cs b/src/MsieJavaScriptEngine/Constants/SpecialMemberName.cs index 5415c70..9febe74 100644 --- a/src/MsieJavaScriptEngine/Constants/SpecialMemberName.cs +++ b/src/MsieJavaScriptEngine/Constants/SpecialMemberName.cs @@ -1,4 +1,5 @@ -namespace MsieJavaScriptEngine.Constants +#if NETFRAMEWORK +namespace MsieJavaScriptEngine.Constants { /// /// Special member names @@ -7,4 +8,5 @@ internal static class SpecialMemberName { public const string Default = "[DISPID=0]"; } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Extensions/ExceptionExtensions.cs b/src/MsieJavaScriptEngine/Extensions/ExceptionExtensions.cs new file mode 100644 index 0000000..9f23504 --- /dev/null +++ b/src/MsieJavaScriptEngine/Extensions/ExceptionExtensions.cs @@ -0,0 +1,29 @@ +#if NET40 +using System; +using System.Reflection; + +namespace MsieJavaScriptEngine.Extensions +{ + /// + /// Exception extensions + /// + internal static class ExceptionExtensions + { + /// + /// Preserves a stack trace of exception + /// + /// The exception + public static void PreserveStackTrace(this Exception source) + { + if (source is 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/MsieJavaScriptEngine/Extensions/StringExtensions.cs b/src/MsieJavaScriptEngine/Extensions/StringExtensions.cs new file mode 100644 index 0000000..ad006d1 --- /dev/null +++ b/src/MsieJavaScriptEngine/Extensions/StringExtensions.cs @@ -0,0 +1,165 @@ +using System; + +namespace MsieJavaScriptEngine.Extensions +{ + /// + /// Extensions for String + /// + internal static class StringExtensions + { + /// + /// Array of strings used to find the newline + /// + private static readonly string[] _newLineStrings = ["\r\n", "\r", "\n"]; + + + /// + /// 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 is null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (value is 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 is null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (trimString is 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; + } +#if NETFRAMEWORK + + /// + /// Converts a first letter of string to capital + /// + /// Instance of + /// The string starting with a capital letter + public static string CapitalizeFirstLetter(this string source) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + + int length = source.Length; + if (length == 0) + { + return source; + } + + string result; + char firstCharacter = source[0]; + + if (char.IsLower(firstCharacter)) + { + result = char.ToUpperInvariant(firstCharacter).ToString(); + if (length > 1) + { + result += source.Substring(1); + } + } + else + { + result = source; + } + + return result; + } +#endif + + /// + /// Splits a string into lines + /// + /// Instance of + /// to omit empty array + /// elements from the array returned; or to include empty + /// array elements in the array returned + /// An array of lines + public static string[] SplitToLines(this string source, StringSplitOptions options) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + + string[] result = source.Split(_newLineStrings, options); + + 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 + public static bool TryGetChar(this string source, int index, out char result) + { + if (source is 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/MsieJavaScriptEngine/Extensions/TypeExtensions.cs b/src/MsieJavaScriptEngine/Extensions/TypeExtensions.cs new file mode 100644 index 0000000..f3b5ba3 --- /dev/null +++ b/src/MsieJavaScriptEngine/Extensions/TypeExtensions.cs @@ -0,0 +1,102 @@ +using System; +#if NETSTANDARD1_3 +using System.Reflection; +#endif + +namespace MsieJavaScriptEngine.Extensions +{ + /// + /// Type extensions + /// + internal 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 is 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/MsieJavaScriptEngine/GlobalUsings.cs b/src/MsieJavaScriptEngine/GlobalUsings.cs new file mode 100644 index 0000000..70bfca9 --- /dev/null +++ b/src/MsieJavaScriptEngine/GlobalUsings.cs @@ -0,0 +1,5 @@ +#if NET10_0_OR_GREATER +global using Lock = System.Threading.Lock; +#else +global using Lock = System.Object; +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Helpers/CallStackItem.cs b/src/MsieJavaScriptEngine/Helpers/CallStackItem.cs new file mode 100644 index 0000000..535a6e3 --- /dev/null +++ b/src/MsieJavaScriptEngine/Helpers/CallStackItem.cs @@ -0,0 +1,56 @@ +namespace MsieJavaScriptEngine.Helpers +{ + /// + /// Script call stack item + /// + internal sealed class CallStackItem + { + /// + /// 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; + } + + + /// + /// Constructs an instance of the script call stack item + /// + public CallStackItem() + { + FunctionName = string.Empty; + DocumentName = string.Empty; + LineNumber = 0; + ColumnNumber = 0; + } + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Helpers/ComHelpers.cs b/src/MsieJavaScriptEngine/Helpers/ComHelpers.cs index b1ef7d9..62823c1 100644 --- a/src/MsieJavaScriptEngine/Helpers/ComHelpers.cs +++ b/src/MsieJavaScriptEngine/Helpers/ComHelpers.cs @@ -1,8 +1,11 @@ -namespace MsieJavaScriptEngine.Helpers -{ - using System; - using System.Runtime.InteropServices; +#if NETFRAMEWORK +using System; +using System.Runtime.InteropServices; + +using MsieJavaScriptEngine.Constants; +namespace MsieJavaScriptEngine.Helpers +{ /// /// COM helpers /// @@ -24,24 +27,30 @@ public static IntPtr CreateInstanceByClsid(Guid clsid) return pInterface; } - public static IntPtr CreateInstanceByProgId(string progId) + public static bool TryCreateComObject(string progId, string serverName, out T obj) where T : class { - Guid clsid = ClsidFromProgId(progId); - IntPtr pInterface = CreateInstanceByClsid(clsid); + Type type; + if (!TryGetComType(progId, serverName, out type)) + { + obj = null; + return false; + } - return pInterface; + obj = Activator.CreateInstance(type) as T; + + return obj is not null; } - private static Guid ClsidFromProgId(string progId) + public static bool TryGetComType(string progId, string serverName, out Type type) { Guid clsid; + type = Guid.TryParseExact(progId, "B", out clsid) ? + Type.GetTypeFromCLSID(clsid, serverName) + : + Type.GetTypeFromProgID(progId, serverName) + ; - if (!Guid.TryParseExact(progId, "B", out clsid)) - { - HResult.Check(NativeMethods.CLSIDFromProgID(progId, out clsid)); - } - - return clsid; + return type is not null; } public static IntPtr QueryInterface(IntPtr pUnknown) @@ -54,6 +63,15 @@ public static IntPtr QueryInterface(IntPtr pUnknown) return pInterface; } + public static IntPtr QueryInterfaceNoThrow(IntPtr pUnknown) + { + IntPtr pInterface; + Guid iid = typeof(T).GUID; + int result = Marshal.QueryInterface(pUnknown, ref iid, out pInterface); + + return result == ComErrorCode.S_OK ? pInterface : IntPtr.Zero; + } + public static void ReleaseAndEmpty(ref IntPtr pUnk) { if (pUnk != IntPtr.Zero) @@ -63,32 +81,6 @@ public static void ReleaseAndEmpty(ref IntPtr pUnk) } } - public static void ReleaseComObject(ref T obj, bool final = false) where T : class - { - if (obj != null && Marshal.IsComObject(obj)) - { - if (final) - { - Marshal.FinalReleaseComObject(obj); - } - else - { - Marshal.ReleaseComObject(obj); - } - } - - obj = null; - } - - #region Private methods - - private static int UnsignedAsSigned(uint value) - { - return BitConverter.ToInt32(BitConverter.GetBytes(value), 0); - } - - #endregion - #region Nested type: NativeMethods private static class NativeMethods @@ -101,12 +93,6 @@ public static extern uint CoCreateInstance( [In] ref Guid iid, [Out] out IntPtr pInterface ); - - [DllImport("ole32.dll", ExactSpelling = true)] - public static extern uint CLSIDFromProgID( - [In] [MarshalAs(UnmanagedType.LPWStr)] string progId, - [Out] out Guid clsid - ); } #endregion @@ -115,14 +101,9 @@ [Out] out Guid clsid public static class HResult { - // ReSharper disable InconsistentNaming - public const int SEVERITY_SUCCESS = 0; - public const int SEVERITY_ERROR = 1; - // ReSharper restore InconsistentNaming - public static void Check(uint result) { - Check(UnsignedAsSigned(result)); + Check(NumericHelpers.UnsignedAsSigned(result)); } public static void Check(int result) @@ -130,9 +111,14 @@ public static void Check(int result) Marshal.ThrowExceptionForHR(result); } + public static bool Succeeded(uint result) + { + return GetSeverity(result) == ComErrorCode.SEVERITY_SUCCESS; + } + public static int GetSeverity(uint result) { - return GetSeverity(UnsignedAsSigned(result)); + return GetSeverity(NumericHelpers.UnsignedAsSigned(result)); } public static int GetSeverity(int result) @@ -142,7 +128,7 @@ public static int GetSeverity(int result) public static int GetFacility(uint result) { - return GetFacility(UnsignedAsSigned(result)); + return GetFacility(NumericHelpers.UnsignedAsSigned(result)); } public static int GetFacility(int result) @@ -152,7 +138,7 @@ public static int GetFacility(int result) public static int GetCode(uint result) { - return GetCode(UnsignedAsSigned(result)); + return GetCode(NumericHelpers.UnsignedAsSigned(result)); } public static int GetCode(int result) @@ -162,10 +148,11 @@ public static int GetCode(int result) public static int MakeResult(int severity, int facility, int code) { - return UnsignedAsSigned((uint)(code & 0xFFFF) | ((uint)(facility & 0x1FFF) << 16) | ((uint)(severity & 0x1) << 31)); + return NumericHelpers.UnsignedAsSigned((uint)(code & 0xFFFF) | ((uint)(facility & 0x1FFF) << 16) | ((uint)(severity & 0x1) << 31)); } } #endregion } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Helpers/CommonRegExps.cs b/src/MsieJavaScriptEngine/Helpers/CommonRegExps.cs new file mode 100644 index 0000000..4e69b8b --- /dev/null +++ b/src/MsieJavaScriptEngine/Helpers/CommonRegExps.cs @@ -0,0 +1,24 @@ +namespace MsieJavaScriptEngine.Helpers +{ + /// + /// Common regular expressions + /// + internal static class CommonRegExps + { + /// + /// Pattern for working with JS names + /// + public const 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 const string JsFullNamePattern = JsNamePattern + @"(?:\." + JsNamePattern + @")*"; + + /// + /// Pattern for working with document names + /// + public const string DocumentNamePattern = @"[^\s*?""<>|][^\t\n\r*?""<>|]*?"; + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Helpers/JsEngineModeHelpers.cs b/src/MsieJavaScriptEngine/Helpers/JsEngineModeHelpers.cs index 6aa48a9..06cfe76 100644 --- a/src/MsieJavaScriptEngine/Helpers/JsEngineModeHelpers.cs +++ b/src/MsieJavaScriptEngine/Helpers/JsEngineModeHelpers.cs @@ -1,7 +1,7 @@ -namespace MsieJavaScriptEngine.Helpers -{ - using System; +using System; +namespace MsieJavaScriptEngine.Helpers +{ internal static class JsEngineModeHelpers { public static string GetModeName(JsEngineMode mode) diff --git a/src/MsieJavaScriptEngine/Helpers/JsErrorHelpers.cs b/src/MsieJavaScriptEngine/Helpers/JsErrorHelpers.cs index 2942f10..27fbf26 100644 --- a/src/MsieJavaScriptEngine/Helpers/JsErrorHelpers.cs +++ b/src/MsieJavaScriptEngine/Helpers/JsErrorHelpers.cs @@ -1,79 +1,599 @@ -namespace MsieJavaScriptEngine.Helpers -{ - using System; - using System.Globalization; - using System.Text; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Text; +using System.Text.RegularExpressions; + +using AdvancedStringBuilder; - using Resources; - using Utilities; +using MsieJavaScriptEngine.Extensions; +using MsieJavaScriptEngine.Resources; +using MsieJavaScriptEngine.Utilities; +namespace MsieJavaScriptEngine.Helpers +{ /// - /// JavaScript engine error helpers + /// JS error helpers /// public static class JsErrorHelpers { + #region Call stack + + /// + /// Regular expression for working with line of the script call stack + /// + private static readonly Regex _callStackLineRegex = + new Regex(@"^[ ]{3}at " + + @"(?[\w][\w ]*|" + CommonRegExps.JsFullNamePattern + @") " + + @"\((?" + CommonRegExps.DocumentNamePattern + @"):" + + @"(?\d+):(?\d+)\)$"); + + + /// + /// Gets a string representation of the script call stack + /// + /// Error message with the script call stack + /// Error message without the script call stack + /// String representation of the script call stack + internal static string GetCallStackFromMessage(string message, string messageWithoutCallStack) + { + if (string.IsNullOrWhiteSpace(message)) + { + return string.Empty; + } + + if (string.IsNullOrWhiteSpace(messageWithoutCallStack)) + { + return message; + } + + string callStack = message + .TrimStart(messageWithoutCallStack) + .TrimStart(EnvironmentShortcuts.NewLineChars) + ; + + return callStack; + } + + /// + /// Parses a string representation of the script call stack to produce an array of + /// instances + /// + /// String representation of the script call stack + /// An array of instances + internal static CallStackItem[] ParseCallStack(string callStack) + { + if (string.IsNullOrWhiteSpace(callStack)) + { + return []; + } + + string[] lines = callStack.SplitToLines(StringSplitOptions.RemoveEmptyEntries); + int lineCount = lines.Length; + var callStackItems = new List(lineCount); + + foreach (string line in lines) + { + CallStackItem callStackItem = MapCallStackItem(line); + if (callStackItem is not null) + { + callStackItems.Add(callStackItem); + } + else + { + Debug.WriteLine(string.Format(CommonStrings.Runtime_InvalidCallStackLineFormat, line)); + return []; + } + } + + return callStackItems.ToArray(); + } + + private static CallStackItem MapCallStackItem(string callStackLine) + { + CallStackItem item = null; + Match lineMatch = _callStackLineRegex.Match(callStackLine); + + if (lineMatch.Success) + { + GroupCollection lineGroups = lineMatch.Groups; + item = new CallStackItem + { + FunctionName = lineGroups["functionName"].Value, + DocumentName = lineGroups["documentName"].Value, + LineNumber = int.Parse(lineGroups["lineNumber"].Value), + ColumnNumber = int.Parse(lineGroups["columnNumber"].Value) + }; + } + + return item; + } + + /// + /// Produces a string representation of the script call stack from array of + /// instances + /// + /// An array of instances + /// String representation of the script call stack + internal static string StringifyCallStackItems(CallStackItem[] callStackItems) + { + if (callStackItems is null) + { + throw new ArgumentException(nameof(callStackItems)); + } + + int stackItemCount = callStackItems.Length; + if (stackItemCount == 0) + { + return string.Empty; + } + + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder stackBuilder = stringBuilderPool.Rent(); + + for (int stackItemIndex = 0; stackItemIndex < stackItemCount; stackItemIndex++) + { + CallStackItem stackItem = callStackItems[stackItemIndex]; + + if (stackItemIndex > 0) + { + stackBuilder.AppendLine(); + } + WriteErrorLocationLine(stackBuilder, stackItem.FunctionName, stackItem.DocumentName, + stackItem.LineNumber, stackItem.ColumnNumber); + } + + string callStack = stackBuilder.ToString(); + stringBuilderPool.Return(stackBuilder); + + return callStack; + } + + #endregion + + #region Generation of error messages + + /// + /// Generates a engine load error message + /// + /// Description of error + /// Name of JS engine mode + /// Makes a quote from the description + /// Engine load error message + internal static string GenerateEngineLoadErrorMessage(string description, string engineModeName, + bool quoteDescription = false) + { + if (engineModeName is null) + { + throw new ArgumentNullException(nameof(engineModeName)); + } + + if (string.IsNullOrWhiteSpace(engineModeName)) + { + throw new ArgumentException( + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(engineModeName)), + nameof(engineModeName) + ); + } + + string jsEngineNotLoadedPart = string.Format(CommonStrings.Engine_JsEngineNotLoaded, + engineModeName); + string message; + + if (!string.IsNullOrWhiteSpace(description)) + { + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder messageBuilder = stringBuilderPool.Rent(); + messageBuilder.Append(jsEngineNotLoadedPart); + messageBuilder.Append(" "); + if (quoteDescription) + { + messageBuilder.AppendFormat(CommonStrings.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 + internal 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 + internal 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 + internal static string GenerateScriptErrorMessage(string type, string description, string documentName, + int lineNumber, int columnNumber, string sourceFragment, string callStack) + { + if (description is null) + { + throw new ArgumentNullException(nameof(description)); + } + + if (string.IsNullOrWhiteSpace(description)) + { + throw new ArgumentException( + string.Format(CommonStrings.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 /// - /// JavaScript engine load exception + /// JS exception + /// Flag for whether to omit message /// Detailed error message - public static string Format(JsEngineLoadException jsEngineLoadException) + public static string GenerateErrorDetails(JsException jsException, bool omitMessage = false) { - var errorMessage = new StringBuilder(); - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_Message, - jsEngineLoadException.Message); - if (!string.IsNullOrWhiteSpace(jsEngineLoadException.EngineMode)) + if (jsException is null) { - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_EngineMode, - jsEngineLoadException.EngineMode); + throw new ArgumentNullException(nameof(jsException)); } - return errorMessage.ToString(); + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder detailsBuilder = stringBuilderPool.Rent(); + WriteCommonErrorDetails(detailsBuilder, jsException, omitMessage); + + var jsScriptException = jsException as JsScriptException; + if (jsScriptException is not null) + { + WriteScriptErrorDetails(detailsBuilder, jsScriptException); + + var jsRuntimeException = jsScriptException as JsRuntimeException; + if (jsRuntimeException is not null) + { + WriteRuntimeErrorDetails(detailsBuilder, jsRuntimeException); + } + } + + detailsBuilder.TrimEnd(); + + string errorDetails = detailsBuilder.ToString(); + stringBuilderPool.Return(detailsBuilder); + + return errorDetails; } /// /// Generates a detailed error message /// - /// JavaScript runtime exception + /// JS script exception + /// Flag for whether to omit message /// Detailed error message - public static string Format(JsRuntimeException jsRuntimeException) + public static string GenerateErrorDetails(JsScriptException jsScriptException, + bool omitMessage = false) { - var errorMessage = new StringBuilder(); - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_Message, - jsRuntimeException.Message); - if (!string.IsNullOrWhiteSpace(jsRuntimeException.EngineMode)) + if (jsScriptException is null) { - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_EngineMode, - jsRuntimeException.EngineMode); + throw new ArgumentNullException(nameof(jsScriptException)); } - if (!string.IsNullOrWhiteSpace(jsRuntimeException.ErrorCode)) + + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder detailsBuilder = stringBuilderPool.Rent(); + WriteCommonErrorDetails(detailsBuilder, jsScriptException, omitMessage); + WriteScriptErrorDetails(detailsBuilder, jsScriptException); + + var jsRuntimeException = jsScriptException as JsRuntimeException; + if (jsRuntimeException is not null) { - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_ErrorCode, - jsRuntimeException.ErrorCode); + WriteRuntimeErrorDetails(detailsBuilder, jsRuntimeException); } - if (!string.IsNullOrWhiteSpace(jsRuntimeException.Category)) + + 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 is null) { - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_Category, - jsRuntimeException.Category); + throw new ArgumentNullException(nameof(jsRuntimeException)); } - if (jsRuntimeException.LineNumber > 0) + + 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}", CommonStrings.ErrorDetails_Message, + jsException.Message); + } + if (!string.IsNullOrWhiteSpace(jsException.EngineMode)) { - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_LineNumber, - jsRuntimeException.LineNumber.ToString(CultureInfo.InvariantCulture)); + buffer.AppendFormatLine("{0}: {1}", CommonStrings.ErrorDetails_EngineMode, + jsException.EngineMode); } - if (jsRuntimeException.ColumnNumber > 0) + if (!string.IsNullOrWhiteSpace(jsException.Category)) { - errorMessage.AppendFormatLine("{0}: {1}", Strings.ErrorDetails_ColumnNumber, - jsRuntimeException.ColumnNumber.ToString(CultureInfo.InvariantCulture)); + buffer.AppendFormatLine("{0}: {1}", CommonStrings.ErrorDetails_Category, + jsException.Category); } - if (!string.IsNullOrWhiteSpace(jsRuntimeException.SourceFragment)) + if (!string.IsNullOrWhiteSpace(jsException.Description)) { - errorMessage.AppendFormatLine("{1}:{0}{0}{2}", Environment.NewLine, - Strings.ErrorDetails_SourceFragment, - jsRuntimeException.SourceFragment); + buffer.AppendFormatLine("{0}: {1}", CommonStrings.ErrorDetails_Description, + jsException.Description); } + } - return errorMessage.ToString(); + /// + /// 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}", CommonStrings.ErrorDetails_Type, + jsScriptException.Type); + } + if (!string.IsNullOrWhiteSpace(jsScriptException.DocumentName)) + { + buffer.AppendFormatLine("{0}: {1}", CommonStrings.ErrorDetails_DocumentName, + jsScriptException.DocumentName); + } + if (jsScriptException.LineNumber > 0) + { + buffer.AppendFormatLine("{0}: {1}", CommonStrings.ErrorDetails_LineNumber, + jsScriptException.LineNumber.ToString(CultureInfo.InvariantCulture)); + } + if (jsScriptException.ColumnNumber > 0) + { + buffer.AppendFormatLine("{0}: {1}", CommonStrings.ErrorDetails_ColumnNumber, + jsScriptException.ColumnNumber.ToString(CultureInfo.InvariantCulture)); + } + if (!string.IsNullOrWhiteSpace(jsScriptException.SourceFragment)) + { + buffer.AppendFormatLine("{0}: {1}", CommonStrings.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}{0}{2}", Environment.NewLine, + CommonStrings.ErrorDetails_CallStack, + jsRuntimeException.CallStack); + } + } + + #endregion + + #region Exception wrapping + + public static JsEngineLoadException WrapEngineLoadException(Exception exception, + string engineModeName, bool quoteDescription = false) + { + string description = exception.Message; + string message = GenerateEngineLoadErrorMessage(description, engineModeName, quoteDescription); + + var jsEngineLoadException = new JsEngineLoadException(message, engineModeName, exception) + { + Description = description + }; + + return jsEngineLoadException; + } + + #endregion + + #region Misc + + /// + /// Writes a error location line to the buffer + /// + /// Instance of + /// Function name + /// Document name + /// Line number + /// Column number + /// Source fragment + internal 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); + } + } + } + } + + #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 + + #endregion } } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Helpers/NumericHelpers.cs b/src/MsieJavaScriptEngine/Helpers/NumericHelpers.cs new file mode 100644 index 0000000..7afb9f2 --- /dev/null +++ b/src/MsieJavaScriptEngine/Helpers/NumericHelpers.cs @@ -0,0 +1,57 @@ +using System; + +using MsieJavaScriptEngine.Extensions; + +namespace MsieJavaScriptEngine.Helpers +{ + /// + /// Numeric helpers + /// + internal static class NumericHelpers + { + private const double MAX_INTEGER_IN_DOUBLE = (1L << 53) - 1; + + + /// + /// Casts a double value to the correct type + /// + /// Double value + /// Numeric value with the correct type + public static object CastDoubleValueToCorrectType(double value) + { + if (Math.Round(value) == value) + { + if (Math.Abs(value) <= MAX_INTEGER_IN_DOUBLE) + { + long longValue = Convert.ToInt64(value); + if (longValue >= int.MinValue && longValue <= int.MaxValue) + { + return (int)longValue; + } + + return longValue; + } + } + else + { + float floatValue = Convert.ToSingle(value); + if (value == floatValue) + { + return floatValue; + } + } + + return value; + } + + internal static int UnsignedAsSigned(uint value) + { + return BitConverter.ToInt32(BitConverter.GetBytes(value), 0); + } + + internal static uint SignedAsUnsigned(int value) + { + return BitConverter.ToUInt32(BitConverter.GetBytes(value), 0); + } + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Helpers/ReflectionHelpers.cs b/src/MsieJavaScriptEngine/Helpers/ReflectionHelpers.cs new file mode 100644 index 0000000..c4cb41a --- /dev/null +++ b/src/MsieJavaScriptEngine/Helpers/ReflectionHelpers.cs @@ -0,0 +1,340 @@ +using System; +#if NET45_OR_GREATER || NETSTANDARD || NET10_0_OR_GREATER +using System.Buffers; +#endif +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +#if NET40 + +using PolyfillsForOldDotNet.System.Buffers; +#endif + +using MsieJavaScriptEngine.Utilities; + +namespace MsieJavaScriptEngine.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; + if (instance) + { + bindingFlags |= BindingFlags.Instance; + } + else + { + bindingFlags |= BindingFlags.Static; + } + + 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 is null) + { + return; + } + + Type valueType = value.GetType(); + Type fieldType = field.FieldType; + + if (valueType != fieldType) + { + object convertedValue; + + if (TypeConverter.TryConvertToType(value, fieldType, out convertedValue)) + { + value = convertedValue; + } + } + } + + public static void FixPropertyValueType(ref object value, PropertyInfo property) + { + if (value is null) + { + return; + } + + Type valueType = value.GetType(); + Type propertyType = property.PropertyType; + + if (valueType != propertyType) + { + object convertedValue; + + if (TypeConverter.TryConvertToType(value, propertyType, out convertedValue)) + { + value = convertedValue; + } + } + } + + public static void FixArgumentTypes(ref object[] argValues, ParameterInfo[] parameters) + { + int argCount = argValues.Length; + if (argCount == 0) + { + return; + } + + int parameterCount = parameters.Length; + + for (int argIndex = 0; argIndex < argCount; argIndex++) + { + if (argIndex >= parameterCount) + { + break; + } + + object argValue = argValues[argIndex]; + if (argValue is 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; + } + } + } + } + + public static MethodBase GetBestFitMethod(MethodBase[] methods, object[] argValues) + { + int methodCount = methods.Length; + if (methodCount == 0) + { + return null; + } + + if (methodCount == 1) + { + MethodBase method = methods[0]; + ParameterInfo[] parameters = method.GetParameters(); + + MethodBase bestFitMethod = null; + if (CompareParameterTypes(argValues, parameters, out _)) + { + bestFitMethod = method; + } + + return bestFitMethod; + } + + MethodWithMetadata[] compatibleMethods = null; + int compatibleMethodCount = 0; + + var methodArrayPool = ArrayPool.Shared; + MethodWithMetadata[] buffer = methodArrayPool.Rent(methodCount); + + try + { + foreach (MethodBase method in methods) + { + ParameterInfo[] parameters = method.GetParameters(); + ushort compatibilityScore; + + if (CompareParameterTypes(argValues, parameters, out compatibilityScore)) + { + compatibleMethodCount++; + + int compatibleMethodIndex = compatibleMethodCount - 1; + buffer[compatibleMethodIndex] = new MethodWithMetadata + { + Method = method, + CompatibilityScore = compatibilityScore + }; + } + } + + if (compatibleMethodCount > 0) + { + if (compatibleMethodCount == 1) + { + return buffer[0].Method; + } + + compatibleMethods = new MethodWithMetadata[compatibleMethodCount]; + Array.Copy(buffer, compatibleMethods, compatibleMethodCount); + } + } + finally + { + bool clearArray = compatibleMethodCount > 0; + methodArrayPool.Return(buffer, clearArray); + } + + if (compatibleMethods is not null) + { + MethodWithMetadata bestFitMethod = compatibleMethods + .OrderByDescending(m => m.CompatibilityScore) + .First() + ; + + return bestFitMethod.Method; + } + + return null; + } + + private static bool CompareParameterTypes(object[] argValues, ParameterInfo[] parameters, + out ushort compatibilityScore) + { + int argCount = argValues.Length; + int parameterCount = parameters.Length; + compatibilityScore = 0; + + if (argCount != parameterCount) + { + return false; + } + else if (argCount == 0) + { + compatibilityScore = ushort.MaxValue; + return true; + } + + for (int argIndex = 0; argIndex < argCount; argIndex++) + { + object argValue = argValues[argIndex]; + Type argType = argValue is not null ? argValue.GetType() : typeof(object); + ParameterInfo parameter = parameters[argIndex]; + Type parameterType = parameter.ParameterType; + + if (argType == 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)) + { + return false; + } + } + } + + return true; + } + + + 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 is null && y is null) + { + return true; + } + else if (x is null || y is 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 + ; + } + + public override int GetHashCode(T obj) + { + return obj is not null ? obj.GetHashCode() : 0; + } + + #endregion + } + + private sealed class MethodWithMetadata + { + public MethodBase Method + { + 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/MsieJavaScriptEngine/Helpers/TextHelpers.cs b/src/MsieJavaScriptEngine/Helpers/TextHelpers.cs new file mode 100644 index 0000000..482478c --- /dev/null +++ b/src/MsieJavaScriptEngine/Helpers/TextHelpers.cs @@ -0,0 +1,128 @@ +using System; +using System.Text; + +using AdvancedStringBuilder; + +using MsieJavaScriptEngine.Extensions; +using MsieJavaScriptEngine.Utilities; + +namespace MsieJavaScriptEngine.Helpers +{ + /// + /// Text helpers + /// + internal static class TextHelpers + { + /// + /// Array of characters used to find the next line break + /// + private static readonly char[] _nextLineBreakChars = EnvironmentShortcuts.NewLineChars; + + + /// + /// Gets a fragment from the text line + /// + /// Content of the text line + /// Column number + /// Maximum length of the text fragment + internal static string GetTextFragmentFromLine(string textLine, int columnNumber, + int maxFragmentLength = 100) + { + if (string.IsNullOrEmpty(textLine)) + { + return string.Empty; + } + + string fragment; + int lineLength = textLine.Length; + + if (lineLength > 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 > 0) + { + if (lineLength - fragmentStartPosition < maxFragmentLength) + { + fragmentStartPosition = lineLength - maxFragmentLength; + } + } + else + { + fragmentStartPosition = 0; + } + int fragmentLength = maxFragmentLength; + + if (fragmentStartPosition > 0) + { + startPart = ellipsisSymbol; + } + if (fragmentStartPosition + fragmentLength < lineLength) + { + endPart = ellipsisSymbol; + } + + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder fragmentBuilder = stringBuilderPool.Rent(); + if (startPart.Length > 0) + { + fragmentBuilder.Append(startPart); + } + fragmentBuilder.Append(textLine.Substring(fragmentStartPosition, fragmentLength)); + if (endPart.Length > 0) + { + fragmentBuilder.Append(endPart); + } + + fragment = fragmentBuilder.ToString(); + stringBuilderPool.Return(fragmentBuilder); + } + else + { + fragment = textLine; + } + + return fragment; + } + + /// + /// 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 + internal 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/MsieJavaScriptEngine/Helpers/TypeMappingHelpers.cs b/src/MsieJavaScriptEngine/Helpers/TypeMappingHelpers.cs index 0f94505..1127fd7 100644 --- a/src/MsieJavaScriptEngine/Helpers/TypeMappingHelpers.cs +++ b/src/MsieJavaScriptEngine/Helpers/TypeMappingHelpers.cs @@ -1,10 +1,12 @@ -namespace MsieJavaScriptEngine.Helpers -{ - using System; - using System.Linq; +#if NETFRAMEWORK +using System; +using System.Linq; - using Utilities; +using MsieJavaScriptEngine.ActiveScript; +using MsieJavaScriptEngine.Utilities; +namespace MsieJavaScriptEngine.Helpers +{ /// /// Type mapping helpers /// @@ -15,10 +17,11 @@ internal static class TypeMappingHelpers /// /// The source value /// JavaScript engine mode + /// Flag for whether to allow the usage of reflection API in the script code /// The mapped value - public static object MapToScriptType(object value, JsEngineMode engineMode) + public static object MapToScriptType(object value, JsEngineMode engineMode, bool allowReflection) { - if (value == null) + if (value is null) { return DBNull.Value; } @@ -28,12 +31,13 @@ public static object MapToScriptType(object value, JsEngineMode engineMode) return null; } - if (TypeConverter.IsPrimitiveType(value.GetType())) + Type type = value.GetType(); + if (TypeConverter.IsPrimitiveType(type) || type.FullName == "System.__ComObject") { return value; } - var result = new HostObject(value, engineMode); + var result = new HostObject(value, engineMode, allowReflection); return result; } @@ -43,10 +47,11 @@ public static object MapToScriptType(object value, JsEngineMode engineMode) /// /// The source array /// JavaScript engine mode + /// Flag for whether to allow the usage of reflection API in the script code /// The mapped array - public static object[] MapToScriptType(object[] args, JsEngineMode engineMode) + public static object[] MapToScriptType(object[] args, JsEngineMode engineMode, bool allowReflection) { - return args.Select(arg => MapToScriptType(arg, engineMode)).ToArray(); + return args.Select(arg => MapToScriptType(arg, engineMode, allowReflection)).ToArray(); } /// @@ -56,7 +61,7 @@ public static object[] MapToScriptType(object[] args, JsEngineMode engineMode) /// The mapped value public static object MapToHostType(object value) { - if (value == null) + if (value is null) { return Undefined.Value; } @@ -72,7 +77,7 @@ public static object MapToHostType(object value) } var hostObj = value as HostObject; - object result = hostObj != null ? hostObj.Target : value; + object result = hostObj is not null ? hostObj.Target : value; return result; } @@ -87,4 +92,5 @@ public static object[] MapToHostType(object[] args) return args.Select(MapToHostType).ToArray(); } } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Helpers/ValidationHelpers.cs b/src/MsieJavaScriptEngine/Helpers/ValidationHelpers.cs index de10b6c..eacf30f 100644 --- a/src/MsieJavaScriptEngine/Helpers/ValidationHelpers.cs +++ b/src/MsieJavaScriptEngine/Helpers/ValidationHelpers.cs @@ -1,11 +1,11 @@ -namespace MsieJavaScriptEngine.Helpers -{ - using System; - using System.Linq; - using System.Text.RegularExpressions; +using System; +using System.Linq; +using System.Text.RegularExpressions; - using Utilities; +using MsieJavaScriptEngine.Utilities; +namespace MsieJavaScriptEngine.Helpers +{ /// /// Validation helpers /// @@ -15,21 +15,26 @@ public static class ValidationHelpers /// List of supported types /// private static readonly Type[] _supportedTypes = - { + [ typeof(Undefined), typeof(Boolean), typeof(Int32), typeof(Double), typeof(String) - }; + ]; + + /// + /// Regular expression for working with JS names + /// + private static readonly Regex _jsNameRegex = new Regex("^" + CommonRegExps.JsNamePattern + "$"); /// - /// Regular expression for working with JS-names + /// 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); @@ -41,7 +46,7 @@ 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) { return TypeConverter.IsPrimitiveType(type); @@ -51,10 +56,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/MsieJavaScriptEngine/HostItemBase.cs b/src/MsieJavaScriptEngine/HostItemBase.cs deleted file mode 100644 index ad1970f..0000000 --- a/src/MsieJavaScriptEngine/HostItemBase.cs +++ /dev/null @@ -1,184 +0,0 @@ -namespace MsieJavaScriptEngine -{ - using System; - using System.Globalization; - using System.Linq; - using System.Reflection; - - /// - /// Base class of item, that implements interface - /// - internal abstract class HostItemBase : IReflect - { - /// - /// Target type - /// - protected readonly Type _type; - - /// - /// Target object - /// - protected readonly object _target; - - /// - /// JavaScript engine mode - /// - protected readonly JsEngineMode _engineMode; - - /// - /// List of fields - /// - private readonly FieldInfo[] _fields; - - /// - /// List of properties - /// - private readonly PropertyInfo[] _properties; - - /// - /// List of methods - /// - private readonly MethodInfo[] _methods; - - /// - /// Gets a target object - /// - public object Target - { - get { return _target; } - } - - - /// - /// Constructs an instance of the wrapper for item, that implements interface - /// - /// Target type - /// Target object - /// JavaScript engine mode - /// Flag for whether to allow access to members of the instance - protected HostItemBase(Type type, object target, JsEngineMode engineMode, bool instance) - { - _type = type; - _target = target; - _engineMode = engineMode; - - BindingFlags bindingFlags = BindingFlags.Public; - if (instance) - { - bindingFlags |= BindingFlags.Instance; - } - else - { - bindingFlags |= BindingFlags.Static; - } - - _fields = _type.GetFields(bindingFlags); - _properties = _type.GetProperties(bindingFlags); - _methods = _type.GetMethods(bindingFlags); - } - - - protected abstract object InnerInvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, - object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters); - - protected object InvokeStandardMember(string name, BindingFlags invokeAttr, Binder binder, object target, - object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) - { - BindingFlags processedInvokeAttr = invokeAttr; - if ((processedInvokeAttr.HasFlag(BindingFlags.GetProperty) - || processedInvokeAttr.HasFlag(BindingFlags.PutDispProperty)) - && !_properties.Any(p => p.Name == name) - && _fields.Any(p => p.Name == name)) - { - if (processedInvokeAttr.HasFlag(BindingFlags.GetProperty)) - { - processedInvokeAttr &= ~BindingFlags.GetProperty; - processedInvokeAttr |= BindingFlags.GetField; - } - else if (processedInvokeAttr.HasFlag(BindingFlags.PutDispProperty)) - { - processedInvokeAttr &= ~BindingFlags.PutDispProperty; - processedInvokeAttr |= BindingFlags.SetField; - } - } - - object result = _type.InvokeMember(name, processedInvokeAttr, binder, target, - args, modifiers, culture, namedParameters); - - return result; - } - - #region IReflect implementation - - Type IReflect.UnderlyingSystemType - { - get { throw new NotImplementedException(); } - } - - - FieldInfo IReflect.GetField(string name, BindingFlags bindingAttr) - { - FieldInfo field = _fields.SingleOrDefault(f => f.Name == name); - - return field; - } - - FieldInfo[] IReflect.GetFields(BindingFlags bindingAttr) - { - return _fields; - } - - MemberInfo[] IReflect.GetMember(string name, BindingFlags bindingAttr) - { - throw new NotImplementedException(); - } - - MemberInfo[] IReflect.GetMembers(BindingFlags bindingAttr) - { - throw new NotImplementedException(); - } - - MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr) - { - MethodInfo method = _methods.SingleOrDefault(m => m.Name == name); - - return method; - } - - MethodInfo IReflect.GetMethod(string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) - { - throw new NotImplementedException(); - } - - MethodInfo[] IReflect.GetMethods(BindingFlags bindingAttr) - { - return _methods; - } - - PropertyInfo[] IReflect.GetProperties(BindingFlags bindingAttr) - { - return _properties; - } - - PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr) - { - PropertyInfo property = _properties.SingleOrDefault(p => p.Name == name); - - return property; - } - - PropertyInfo IReflect.GetProperty(string name, BindingFlags bindingAttr, Binder binder, - Type returnType, Type[] types, ParameterModifier[] modifiers) - { - throw new NotImplementedException(); - } - - object IReflect.InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, - object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) - { - return InnerInvokeMember(name, invokeAttr, binder,target, args, modifiers, culture, namedParameters); - } - - #endregion - } -} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/HostObject.cs b/src/MsieJavaScriptEngine/HostObject.cs deleted file mode 100644 index 1c19248..0000000 --- a/src/MsieJavaScriptEngine/HostObject.cs +++ /dev/null @@ -1,70 +0,0 @@ -namespace MsieJavaScriptEngine -{ - using System; - using System.Globalization; - using System.Linq; - using System.Reflection; - - using Constants; - using Helpers; - - /// - /// Wrapper for object, that implements interface - /// - internal sealed class HostObject : HostItemBase - { - /// - /// Constructs an instance of the wrapper for object, that implements interface - /// - /// Target object - /// JavaScript engine mode - public HostObject(object target, JsEngineMode engineMode) - : base(target.GetType(), target, engineMode, true) - { } - - - private object InvokeDelegate(Delegate del, object[] args) - { - if (del == null) - { - throw new ArgumentNullException("del"); - } - - object[] processedArgs = args; - - if (_engineMode == JsEngineMode.Classic && processedArgs.Length > 0) - { - processedArgs = processedArgs.Skip(1).ToArray(); - } - - object result = del.DynamicInvoke(processedArgs); - - return result; - } - - #region HostItemBase implementation - - protected override object InnerInvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, - object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) - { - object result; - object processedTarget = TypeMappingHelpers.MapToHostType(target); - object[] processedArgs = TypeMappingHelpers.MapToHostType(args); - - if (name == SpecialMemberName.Default && processedTarget is Delegate) - { - var del = (Delegate)processedTarget; - result = InvokeDelegate(del, processedArgs); - } - else - { - result = InvokeStandardMember(name, invokeAttr, binder, processedTarget, - processedArgs, modifiers, culture, namedParameters); - } - - return TypeMappingHelpers.MapToScriptType(result, _engineMode); - } - - #endregion - } -} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/IInnerJsEngine.cs b/src/MsieJavaScriptEngine/IInnerJsEngine.cs index a733dc1..6198864 100644 --- a/src/MsieJavaScriptEngine/IInnerJsEngine.cs +++ b/src/MsieJavaScriptEngine/IInnerJsEngine.cs @@ -1,30 +1,52 @@ -namespace MsieJavaScriptEngine -{ - using System; +using System; +namespace MsieJavaScriptEngine +{ /// - /// Interface for the inner JavaScript engine + /// Interface for the inner JS engine /// internal interface IInnerJsEngine : IDisposable { /// - /// Gets a name of JavaScript engine mode + /// Gets a name of JS engine mode /// string Mode { get; } + /// + /// Gets a value that indicates if the JS engine supports script pre-compilation + /// + bool SupportsScriptPrecompilation { get; } + + + /// + /// 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 + PrecompiledScript Precompile(string code, string documentName); /// /// Evaluates an expression /// - /// JavaScript expression + /// JS expression + /// Document name /// Result of the expression - object Evaluate(string expression); + object Evaluate(string expression, string documentName); /// /// Executes a code /// - /// JavaScript code - void Execute(string code); + /// 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(PrecompiledScript precompiledScript); /// /// Calls a function @@ -35,10 +57,10 @@ internal interface IInnerJsEngine : IDisposable object CallFunction(string functionName, params object[] args); /// - /// Сhecks for the existence of a variable + /// Checks for the existence of a variable /// /// Name of variable - /// Result of check (true - exists; false - not exists + /// Result of check (true - exists; false - not exists bool HasVariable(string variableName); /// @@ -64,20 +86,32 @@ internal interface IInnerJsEngine : IDisposable /// /// 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/MsieJavaScriptEngine/InnerJsEngineBase.cs b/src/MsieJavaScriptEngine/InnerJsEngineBase.cs new file mode 100644 index 0000000..e68123a --- /dev/null +++ b/src/MsieJavaScriptEngine/InnerJsEngineBase.cs @@ -0,0 +1,99 @@ +using System; + +using MsieJavaScriptEngine.Helpers; +using MsieJavaScriptEngine.Utilities; + +namespace MsieJavaScriptEngine +{ + /// + /// Base class of the inner JS engine + /// + internal abstract class InnerJsEngineBase : IInnerJsEngine + { + /// + /// JS engine settings + /// + protected JsEngineSettings _settings; + + /// + /// Name of JS engine mode + /// + protected readonly string _engineModeName; + + /// + /// Flag that object is destroyed + /// + protected StatedFlag _disposedFlag = new StatedFlag(); + + + /// + /// Constructs an instance of the inner JS engine + /// + /// JS engine settings + protected InnerJsEngineBase(JsEngineSettings settings) + { + _settings = settings; + _engineModeName = JsEngineModeHelpers.GetModeName(_settings.EngineMode); + } + + + #region IInnerJsEngine implementation + + /// + public string Mode + { + get { return _engineModeName; } + } + + /// + public abstract bool SupportsScriptPrecompilation { get; } + + + /// + public abstract PrecompiledScript Precompile(string code, string documentName); + + /// + public abstract object Evaluate(string expression, string documentName); + + /// + public abstract void Execute(string code, string documentName); + + /// + public abstract void Execute(PrecompiledScript precompiledScript); + + /// + public abstract object CallFunction(string functionName, params object[] args); + + /// + public abstract bool HasVariable(string variableName); + + /// + public abstract object GetVariableValue(string variableName); + + /// + public abstract void SetVariableValue(string variableName, object value); + + /// + public abstract void RemoveVariable(string variableName); + + /// + public abstract void EmbedHostObject(string itemName, object value); + + /// + public abstract void EmbedHostType(string itemName, Type type); + + /// + public abstract void Interrupt(); + + /// + public abstract void CollectGarbage(); + + #endregion + + #region IDisposable implementation + + public abstract void Dispose(); + + #endregion + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsCompilationException.cs b/src/MsieJavaScriptEngine/JsCompilationException.cs new file mode 100644 index 0000000..b8465d2 --- /dev/null +++ b/src/MsieJavaScriptEngine/JsCompilationException.cs @@ -0,0 +1,80 @@ +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#endif + +using MsieJavaScriptEngine.Constants; + +namespace MsieJavaScriptEngine +{ + /// + /// 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 mode + public JsCompilationException(string message, string engineMode) + : base(message, engineMode) + { + Category = JsErrorCategory.Compilation; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine mode + /// The exception that is the cause of the current exception + public JsCompilationException(string message, string engineMode, Exception innerException) + : base(message, engineMode, 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 +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif + private JsCompilationException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsEngineException.cs b/src/MsieJavaScriptEngine/JsEngineException.cs new file mode 100644 index 0000000..f82bc47 --- /dev/null +++ b/src/MsieJavaScriptEngine/JsEngineException.cs @@ -0,0 +1,79 @@ +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#endif + +using MsieJavaScriptEngine.Constants; + +namespace MsieJavaScriptEngine +{ + /// + /// 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 mode + public JsEngineException(string message, string engineMode) + : base(message, engineMode) + { + Category = JsErrorCategory.Engine; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine mode + /// The exception that is the cause of the current exception + public JsEngineException(string message, string engineMode, Exception innerException) + : base(message, engineMode, 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 +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif + protected JsEngineException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsEngineLoadException.cs b/src/MsieJavaScriptEngine/JsEngineLoadException.cs index 19faf1b..a6b7084 100644 --- a/src/MsieJavaScriptEngine/JsEngineLoadException.cs +++ b/src/MsieJavaScriptEngine/JsEngineLoadException.cs @@ -1,11 +1,19 @@ -namespace MsieJavaScriptEngine -{ - using System; +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#endif + +using MsieJavaScriptEngine.Constants; +namespace MsieJavaScriptEngine +{ /// - /// 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,38 +21,59 @@ public sealed class JsEngineLoadException : JsException /// /// The message that describes the error public JsEngineLoadException(string message) - : this(message, string.Empty) - { } + : 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, 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 mode + /// Name of JS engine mode public JsEngineLoadException(string message, string engineMode) - : this(message, engineMode, null) - { } + : base(message, engineMode) + { + 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 mode + /// Name of JS engine mode /// The exception that is the cause of the current exception public JsEngineLoadException(string message, string engineMode, Exception innerException) : base(message, engineMode, 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 +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif + private JsEngineLoadException(SerializationInfo info, StreamingContext context) + : base(info, context) { } +#endif } } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsEngineMode.cs b/src/MsieJavaScriptEngine/JsEngineMode.cs index 495b99d..172c2b8 100644 --- a/src/MsieJavaScriptEngine/JsEngineMode.cs +++ b/src/MsieJavaScriptEngine/JsEngineMode.cs @@ -1,36 +1,38 @@ namespace MsieJavaScriptEngine { /// - /// JavaScript engine modes + /// 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 5). + /// 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). + /// “IE” JsRT version of Chakra JS engine (supports ECMAScript 5). /// Requires Internet Explorer 11 or Microsoft Edge on the machine. /// ChakraIeJsRt, /// - /// “Edge” JsRT version of Chakra JavaScript engine (supports ECMAScript 5). + /// “Edge” JsRT version of Chakra JS engine (supports ECMAScript 5). /// Requires Microsoft Edge on the machine. /// ChakraEdgeJsRt diff --git a/src/MsieJavaScriptEngine/JsEngineSettings.cs b/src/MsieJavaScriptEngine/JsEngineSettings.cs index 4102a28..ff92ee5 100644 --- a/src/MsieJavaScriptEngine/JsEngineSettings.cs +++ b/src/MsieJavaScriptEngine/JsEngineSettings.cs @@ -1,13 +1,47 @@ -namespace MsieJavaScriptEngine +using System; + +using MsieJavaScriptEngine.Resources; +using MsieJavaScriptEngine.Utilities; + +namespace MsieJavaScriptEngine { /// - /// JavaScript engine settings + /// JS engine settings /// public sealed class JsEngineSettings { +#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 - /// (only works in the ChakraIeJsRt and ChakraEdgeJsRt modes) /// public bool EnableDebugging { @@ -16,13 +50,39 @@ public bool EnableDebugging } /// - /// Gets or sets a JavaScript engine mode + /// 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), + CommonStrings.Engine_MaxStackSizeMustBeNonNegative + ); + } + + _maxStackSize = value; + } + } +#endif /// /// Gets or sets a flag for whether to use the ECMAScript 5 Polyfill @@ -44,14 +104,28 @@ public bool UseJson2Library /// - /// Constructs instance of JavaScript engine settings + /// Constructs an instance of JS engine settings /// public JsEngineSettings() { + AllowReflection = false; EnableDebugging = false; EngineMode = JsEngineMode.Auto; +#if !NETSTANDARD1_3 + MaxStackSize = Utils.Is64BitProcess() ? STACK_SIZE_64 : STACK_SIZE_32; +#endif UseEcmaScript5Polyfill = false; UseJson2Library = false; } + + + /// + /// Creates a new object that is a copy of the current instance + /// + /// A new object that is a copy of this instance + public JsEngineSettings Clone() + { + return (JsEngineSettings)MemberwiseClone(); + } } } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsException.cs b/src/MsieJavaScriptEngine/JsException.cs index 85808f6..e97d5f2 100644 --- a/src/MsieJavaScriptEngine/JsException.cs +++ b/src/MsieJavaScriptEngine/JsException.cs @@ -1,19 +1,66 @@ -namespace MsieJavaScriptEngine -{ - using System; +using System; +using System.Text; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#if !NET10_0_OR_GREATER +using System.Security.Permissions; +#endif +#endif + +using AdvancedStringBuilder; +using MsieJavaScriptEngine.Constants; +using MsieJavaScriptEngine.Helpers; + +namespace MsieJavaScriptEngine +{ /// - /// 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 mode + /// Name of JS engine mode + /// + private readonly string _engineMode = string.Empty; + + /// + /// Error category + /// + private string _category = JsErrorCategory.Unknown; + + /// + /// Description of error + /// + private string _description = string.Empty; + + /// + /// Gets a name of JS engine mode /// public string EngineMode { - get; - private set; + get { return _engineMode; } + } + + /// + /// 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; } } @@ -23,40 +70,131 @@ public string EngineMode /// /// The message that describes the error public JsException(string message) - : this(message, 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, 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 mode + /// Name of JS engine mode public JsException(string message, string engineMode) : this(message, engineMode, 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 mode + /// Name of JS engine mode /// The exception that is the cause of the current exception public JsException(string message, string engineMode, Exception innerException) : base(message, innerException) { - EngineMode = engineMode; + _engineMode = engineMode; } +#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 +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif + protected JsException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + if (info is not null) + { + _engineMode = info.GetString("EngineMode"); + _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 +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#else + [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] +#endif + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info is null) + { + throw new ArgumentNullException(nameof(info)); + } + + base.GetObjectData(info, context); + info.AddValue("EngineMode", _engineMode); + 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 is not null) + { + resultBuilder.Append(" ---> "); + resultBuilder.Append(this.InnerException.ToString()); + } + + if (this.StackTrace is not 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/MsieJavaScriptEngine/JsFatalException.cs b/src/MsieJavaScriptEngine/JsFatalException.cs new file mode 100644 index 0000000..43f2400 --- /dev/null +++ b/src/MsieJavaScriptEngine/JsFatalException.cs @@ -0,0 +1,79 @@ +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#endif + +using MsieJavaScriptEngine.Constants; + +namespace MsieJavaScriptEngine +{ + /// + /// 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 mode + public JsFatalException(string message, string engineMode) + : base(message, engineMode) + { + Category = JsErrorCategory.Fatal; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine mode + /// The exception that is the cause of the current exception + public JsFatalException(string message, string engineMode, Exception innerException) + : base(message, engineMode, 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 +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif + private JsFatalException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsInterruptedException.cs b/src/MsieJavaScriptEngine/JsInterruptedException.cs new file mode 100644 index 0000000..ec40bad --- /dev/null +++ b/src/MsieJavaScriptEngine/JsInterruptedException.cs @@ -0,0 +1,79 @@ +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#endif + +using MsieJavaScriptEngine.Constants; + +namespace MsieJavaScriptEngine +{ + /// + /// 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 mode + public JsInterruptedException(string message, string engineMode) + : base(message, engineMode) + { + Category = JsErrorCategory.Interrupted; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine mode + /// The exception that is the cause of the current exception + public JsInterruptedException(string message, string engineMode, Exception innerException) + : base(message, engineMode, 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 +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif + private JsInterruptedException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/ChakraJsRtJsEngineBase.cs b/src/MsieJavaScriptEngine/JsRt/ChakraJsRtJsEngineBase.cs index d724440..3c467d9 100644 --- a/src/MsieJavaScriptEngine/JsRt/ChakraJsRtJsEngineBase.cs +++ b/src/MsieJavaScriptEngine/JsRt/ChakraJsRtJsEngineBase.cs @@ -1,90 +1,35 @@ -namespace MsieJavaScriptEngine.JsRt -{ - using System; - - using Helpers; +using System; +namespace MsieJavaScriptEngine.JsRt +{ /// - /// Base class of the Chakra JsRT JavaScript engine + /// JsRT version of Chakra JS engine /// - internal abstract class ChakraJsRtJsEngineBase : IInnerJsEngine + internal abstract class ChakraJsRtJsEngineBase : InnerJsEngineBase { /// - /// JavaScript engine mode - /// - protected readonly JsEngineMode _engineMode; - - /// - /// Name of JavaScript engine mode - /// - protected readonly string _engineModeName; - - /// - /// Flag for whether to enable script debugging features - /// - protected readonly bool _enableDebugging; - - /// - /// Flag indicating whether debugging started + /// JS source context /// - private StatedFlag _debuggingStartedFlag; - + protected JsSourceContext _jsSourceContext = JsSourceContext.FromIntPtr(IntPtr.Zero); /// - /// Constructs instance of the Chakra JsRT JavaScript engine + /// Script dispatcher /// - /// JavaScript engine mode - /// Flag for whether to enable script debugging features - protected ChakraJsRtJsEngineBase(JsEngineMode engineMode, bool enableDebugging) - { - _engineMode = engineMode; - _engineModeName = JsEngineModeHelpers.GetModeName(engineMode); - _enableDebugging = enableDebugging; - } + protected ScriptDispatcher _dispatcher; /// - /// Starts debugging + /// Constructs an instance of the Chakra JsRT engine /// - protected void StartDebugging() + /// JS engine settings + protected ChakraJsRtJsEngineBase(JsEngineSettings settings) + : base(settings) { - if (_debuggingStartedFlag.Set()) - { - InnerStartDebugging(); - } +#if NETSTANDARD1_3 + _dispatcher = new ScriptDispatcher(); +#else + _dispatcher = new ScriptDispatcher(settings.MaxStackSize); +#endif } - - protected abstract void InnerStartDebugging(); - - #region IInnerJsEngine implementation - - public abstract string Mode { get; } - - - public abstract object Evaluate(string expression); - - public abstract void Execute(string code); - - public abstract object CallFunction(string functionName, params object[] args); - - public abstract bool HasVariable(string variableName); - - public abstract object GetVariableValue(string variableName); - - public abstract void SetVariableValue(string variableName, object value); - - public abstract void RemoveVariable(string variableName); - - public abstract void EmbedHostObject(string itemName, object value); - - public abstract void EmbedHostType(string itemName, Type type); - - #endregion - - #region IDisposable implementation - - public abstract void Dispose(); - - #endregion } } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Edge/ChakraEdgeJsRtJsEngine.cs b/src/MsieJavaScriptEngine/JsRt/Edge/ChakraEdgeJsRtJsEngine.cs index 2d95218..4f0b1c2 100644 --- a/src/MsieJavaScriptEngine/JsRt/Edge/ChakraEdgeJsRtJsEngine.cs +++ b/src/MsieJavaScriptEngine/JsRt/Edge/ChakraEdgeJsRtJsEngine.cs @@ -1,84 +1,107 @@ -namespace MsieJavaScriptEngine.JsRt.Edge +using System; + +using MsieJavaScriptEngine.Constants; +using MsieJavaScriptEngine.Extensions; +using MsieJavaScriptEngine.Helpers; +using MsieJavaScriptEngine.Resources; + +using WrapperCompilationException = MsieJavaScriptEngine.JsCompilationException; +using WrapperEngineException = MsieJavaScriptEngine.JsEngineException; +using WrapperEngineLoadException = MsieJavaScriptEngine.JsEngineLoadException; +using WrapperException = MsieJavaScriptEngine.JsException; +using WrapperFatalException = MsieJavaScriptEngine.JsFatalException; +using WrapperInterruptedException = MsieJavaScriptEngine.JsInterruptedException; +using WrapperRuntimeException = MsieJavaScriptEngine.JsRuntimeException; +using WrapperScriptException = MsieJavaScriptEngine.JsScriptException; +using WrapperUsageException = MsieJavaScriptEngine.JsUsageException; + +using OriginalEngineException = MsieJavaScriptEngine.JsRt.JsEngineException; +using OriginalException = MsieJavaScriptEngine.JsRt.JsException; +using OriginalFatalException = MsieJavaScriptEngine.JsRt.JsFatalException; +using OriginalScriptException = MsieJavaScriptEngine.JsRt.Edge.EdgeJsScriptException; +using OriginalUsageException = MsieJavaScriptEngine.JsRt.JsUsageException; + +namespace MsieJavaScriptEngine.JsRt.Edge { - using System; - using System.Globalization; - using System.Linq; - - using Constants; - using Resources; - using Utilities; - /// - /// “Edge” JsRT version of Chakra JavaScript engine + /// “Edge” JsRT version of Chakra JS engine /// internal sealed class ChakraEdgeJsRtJsEngine : ChakraJsRtJsEngineBase { /// - /// Instance of JavaScript runtime + /// Instance of JS runtime /// private EdgeJsRuntime _jsRuntime; /// - /// Instance of JavaScript context + /// Instance of JS context /// - private readonly EdgeJsContext _jsContext; + private EdgeJsContext _jsContext; /// - /// Flag indicating whether this JavaScript engine is supported - /// - private static bool? _isSupported; - - /// - /// Support synchronizer + /// Type mapper /// - private static readonly object _supportSynchronizer = new object(); + private EdgeTypeMapper _typeMapper; /// - /// Run synchronizer + /// Flag indicating whether this JS engine is supported /// - private readonly object _runSynchronizer = new object(); + private static bool? _isSupported; /// - /// Flag that object is destroyed + /// Support synchronizer /// - private StatedFlag _disposedFlag = new StatedFlag(); + private static readonly Lock _supportSynchronizer = new Lock(); /// - /// Constructs instance of the Chakra “Edge” JsRT JavaScript engine + /// Constructs an instance of the Chakra “Edge” JsRT engine /// - /// Flag for whether to enable script debugging features - public ChakraEdgeJsRtJsEngine(bool enableDebugging) - : base(JsEngineMode.ChakraEdgeJsRt, enableDebugging) + /// JS engine settings + public ChakraEdgeJsRtJsEngine(JsEngineSettings settings) + : base(settings) { + _typeMapper = new EdgeTypeMapper(settings.AllowReflection); + try { - _jsRuntime = CreateJsRuntime(); - _jsContext = _jsRuntime.CreateContext(); + _dispatcher.Invoke(() => + { + _jsRuntime = CreateJsRuntime(); + _jsContext = _jsRuntime.CreateContext(); + if (_jsContext.IsValid) + { + _jsContext.AddRef(); + + if (_settings.EnableDebugging) + { + using (new EdgeJsScope(_jsContext)) + { + EdgeJsContext.StartDebugging(); + } + } + } + }); } - catch (JsUsageException e) + catch (DllNotFoundException e) { - string errorMessage; - if (e.ErrorCode == JsErrorCode.WrongThread) - { - errorMessage = Strings.Runtime_JsEnginesConflictOnMachine; - } - else - { - errorMessage = string.Format(Strings.Runtime_EdgeJsEngineNotLoaded, e.Message); - } - - throw new JsEngineLoadException(errorMessage, _engineModeName); + throw WrapDllNotFoundException(e); } catch (Exception e) { - throw new JsEngineLoadException( - string.Format(Strings.Runtime_EdgeJsEngineNotLoaded, e.Message), _engineModeName); + throw JsErrorHelpers.WrapEngineLoadException(e, _engineModeName, true); + } + finally + { + if (!_jsContext.IsValid) + { + Dispose(); + } } } /// - /// Destructs instance of the Chakra “Edge” JsRT JavaScript engine + /// Destructs an instance of the Chakra “Edge” JsRT engine /// ~ChakraEdgeJsRtJsEngine() { @@ -87,20 +110,18 @@ public ChakraEdgeJsRtJsEngine(bool enableDebugging) /// - /// Creates a instance of JavaScript runtime with special settings + /// Creates a instance of JS runtime with special settings /// - /// Instance of JavaScript runtime with special settings + /// Instance of JS runtime with special settings private static EdgeJsRuntime CreateJsRuntime() { - var jsRuntime = EdgeJsRuntime.Create(JsRuntimeAttributes.AllowScriptInterrupt, null); - - return jsRuntime; + return EdgeJsRuntime.Create(JsRuntimeAttributes.AllowScriptInterrupt, null); } /// - /// Checks a support of the Chakra “Edge” JsRT JavaScript engine + /// Checks a support of the Chakra “Edge” JsRT engine /// - /// Result of check (true - supports; false - does not support) + /// Result of check (true - supports; false - does not support) public static bool IsSupported() { if (_isSupported.HasValue) @@ -124,7 +145,7 @@ public static bool IsSupported() } catch (DllNotFoundException e) { - if (e.Message.IndexOf("'" + DllName.Chakra + "'", StringComparison.OrdinalIgnoreCase) != -1) + if (e.Message.ContainsQuotedValue(DllName.Chakra)) { _isSupported = false; } @@ -143,294 +164,438 @@ public static bool IsSupported() } /// - /// Makes a mapping of value from the host type to a script type + /// Adds a reference to the value /// - /// The source value - /// The mapped value - private EdgeJsValue MapToScriptType(object value) + /// The value + private static void AddReferenceToValue(EdgeJsValue value) { - if (value == null) - { - return EdgeJsValue.Null; - } - - if (value is Undefined) + if (CanHaveReferences(value)) { - return EdgeJsValue.Undefined; - } - - var typeCode = Type.GetTypeCode(value.GetType()); - - switch (typeCode) - { - case TypeCode.Boolean: - return EdgeJsValue.FromBoolean((bool)value); - case TypeCode.Int32: - return EdgeJsValue.FromInt32((int)value); - case TypeCode.Double: - return EdgeJsValue.FromDouble((double)value); - case TypeCode.String: - return EdgeJsValue.FromString((string)value); - default: - object processedValue = !TypeConverter.IsPrimitiveType(typeCode) ? - new HostObject(value, _engineMode) : value; - return EdgeJsValue.FromObject(processedValue); + value.AddRef(); } } /// - /// Makes a mapping of array items from the host type to a script type + /// Removes a reference to the value /// - /// The source array - /// The mapped array - private EdgeJsValue[] MapToScriptType(object[] args) + /// The value + private static void RemoveReferenceToValue(EdgeJsValue value) { - return args.Select(MapToScriptType).ToArray(); + if (CanHaveReferences(value)) + { + value.Release(); + } } /// - /// 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(EdgeJsValue value) + /// The value + /// Result of check (true - may have; false - may not have) + private static bool CanHaveReferences(EdgeJsValue value) { JsValueType valueType = value.ValueType; - EdgeJsValue 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 = 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: - processedValue = value.ConvertToObject(); - object obj = processedValue.ToObject(); - - if (!TypeConverter.IsPrimitiveType(obj.GetType())) - { - var hostObj = obj as HostObject; - result = hostObj != null ? hostObj.Target : obj; - } - else - { - result = obj; - } - 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 source array - /// The mapped array - private object[] MapToHostType(EdgeJsValue[] args) - { - return args.Select(MapToHostType).ToArray(); - } + #region Mapping - private JsRuntimeException ConvertJsExceptionToJsRuntimeException( - JsException jsException) + private WrapperException WrapJsException(OriginalException originalException, + string defaultDocumentName = null) { - string message = jsException.Message; - string category = string.Empty; + 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; - var jsScriptException = jsException as EdgeJsScriptException; - if (jsScriptException != null) + var originalScriptException = originalException as OriginalScriptException; + if (originalScriptException is not null) { - category = "Script error"; - EdgeJsValue errorValue = jsScriptException.Error; + EdgeJsValue errorValue = originalScriptException.Error; - EdgeJsPropertyId messagePropertyId = EdgeJsPropertyId.FromString("message"); - EdgeJsValue messagePropertyValue = errorValue.GetProperty(messagePropertyId); - string scriptMessage = messagePropertyValue.ConvertToString().ToString(); - if (!string.IsNullOrWhiteSpace(scriptMessage)) + if (errorValue.IsValid) { - message = string.Format("{0}: {1}", message.TrimEnd('.'), scriptMessage); - } + JsValueType errorValueType = errorValue.ValueType; - EdgeJsPropertyId linePropertyId = EdgeJsPropertyId.FromString("line"); - if (errorValue.HasProperty(linePropertyId)) - { - EdgeJsValue linePropertyValue = errorValue.GetProperty(linePropertyId); - lineNumber = (int)linePropertyValue.ConvertToNumber().ToDouble() + 1; + if (errorValueType == JsValueType.Error + || errorValueType == JsValueType.Object) + { + EdgeJsValue messagePropertyValue = errorValue.GetProperty("message"); + string localDescription = messagePropertyValue.ConvertToString().ToString(); + if (!string.IsNullOrWhiteSpace(localDescription)) + { + description = localDescription; + } + + EdgeJsValue namePropertyValue = errorValue.GetProperty("name"); + type = namePropertyValue.ValueType == JsValueType.String ? + namePropertyValue.ToString() : string.Empty; + + EdgeJsPropertyId descriptionPropertyId = EdgeJsPropertyId.FromString("description"); + if (errorValue.HasProperty(descriptionPropertyId)) + { + EdgeJsValue descriptionPropertyValue = errorValue.GetProperty(descriptionPropertyId); + localDescription = descriptionPropertyValue.ConvertToString().ToString(); + if (!string.IsNullOrWhiteSpace(localDescription)) + { + description = localDescription; + } + } + + if (type == JsErrorType.Syntax) + { + errorCode = JsErrorCode.ScriptCompile; + } + else + { + EdgeJsPropertyId numberPropertyId = EdgeJsPropertyId.FromString("number"); + if (errorValue.HasProperty(numberPropertyId)) + { + EdgeJsValue numberPropertyValue = errorValue.GetProperty(numberPropertyId); + int errorNumber = numberPropertyValue.ValueType == JsValueType.Number ? + numberPropertyValue.ToInt32() : 0; + errorCode = (JsErrorCode)errorNumber; + } + } + + EdgeJsPropertyId stackPropertyId = EdgeJsPropertyId.FromString("stack"); + if (errorValue.HasProperty(stackPropertyId)) + { + EdgeJsValue stackPropertyValue = errorValue.GetProperty(stackPropertyId); + string messageWithTypeAndCallStack = stackPropertyValue.ValueType == JsValueType.String ? + stackPropertyValue.ToString() : string.Empty; + string messageWithType = errorValue.ConvertToString().ToString(); + string rawCallStack = JsErrorHelpers.GetCallStackFromMessage( + messageWithTypeAndCallStack, messageWithType); + CallStackItem[] callStackItems = JsErrorHelpers.ParseCallStack(rawCallStack); + + if (callStackItems.Length > 0) + { + CallStackItem firstCallStackItem = callStackItems[0]; + if (firstCallStackItem.DocumentName.Length > 0) + { + documentName = firstCallStackItem.DocumentName; + } + lineNumber = firstCallStackItem.LineNumber; + columnNumber = firstCallStackItem.ColumnNumber; + callStack = JsErrorHelpers.StringifyCallStackItems(callStackItems); + } + + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, callStack); + } + else + { + EdgeJsPropertyId urlPropertyId = EdgeJsPropertyId.FromString("url"); + if (errorValue.HasProperty(urlPropertyId)) + { + EdgeJsValue urlPropertyValue = errorValue.GetProperty(urlPropertyId); + documentName = urlPropertyValue.ValueType == JsValueType.String ? + urlPropertyValue.ToString() : string.Empty; + } + + EdgeJsPropertyId linePropertyId = EdgeJsPropertyId.FromString("line"); + if (errorValue.HasProperty(linePropertyId)) + { + EdgeJsValue linePropertyValue = errorValue.GetProperty(linePropertyId); + lineNumber = linePropertyValue.ValueType == JsValueType.Number ? + linePropertyValue.ToInt32() + 1 : 0; + } + + EdgeJsPropertyId columnPropertyId = EdgeJsPropertyId.FromString("column"); + if (errorValue.HasProperty(columnPropertyId)) + { + EdgeJsValue columnPropertyValue = errorValue.GetProperty(columnPropertyId); + columnNumber = columnPropertyValue.ValueType == JsValueType.Number ? + columnPropertyValue.ToInt32() + 1 : 0; + } + + string sourceLine = string.Empty; + EdgeJsPropertyId sourcePropertyId = EdgeJsPropertyId.FromString("source"); + if (errorValue.HasProperty(sourcePropertyId)) + { + EdgeJsValue sourcePropertyValue = errorValue.GetProperty(sourcePropertyId); + sourceLine = sourcePropertyValue.ValueType == JsValueType.String ? + sourcePropertyValue.ToString() : string.Empty; + if (sourceLine != "undefined") + { + sourceFragment = TextHelpers.GetTextFragmentFromLine(sourceLine, columnNumber); + } + } + + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, documentName, + lineNumber, columnNumber, sourceFragment); + } + } + else if (errorValueType == JsValueType.String) + { + message = errorValue.ToString(); + description = message; + } + else + { + message = errorValue.ConvertToString().ToString(); + description = message; + } } - EdgeJsPropertyId columnPropertyId = EdgeJsPropertyId.FromString("column"); - if (errorValue.HasProperty(columnPropertyId)) + WrapperScriptException wrapperScriptException; + if (errorCode == JsErrorCode.ScriptCompile) { - EdgeJsValue columnPropertyValue = errorValue.GetProperty(columnPropertyId); - columnNumber = (int)columnPropertyValue.ConvertToNumber().ToDouble() + 1; + wrapperScriptException = new WrapperCompilationException(message, _engineModeName, + originalScriptException); } + else if (errorCode == JsErrorCode.ScriptTerminated) + { + wrapperScriptException = new WrapperInterruptedException(CommonStrings.Runtime_ScriptInterrupted, + _engineModeName, originalScriptException); - EdgeJsPropertyId sourcePropertyId = EdgeJsPropertyId.FromString("source"); - if (errorValue.HasProperty(sourcePropertyId)) + // Restore a JS engine after interruption + _jsRuntime.Disabled = false; + } + else { - EdgeJsValue sourcePropertyValue = errorValue.GetProperty(sourcePropertyId); - sourceFragment = sourcePropertyValue.ConvertToString().ToString(); + wrapperScriptException = new WrapperRuntimeException(message, _engineModeName, + originalScriptException) + { + CallStack = callStack + }; } + wrapperScriptException.Type = type; + wrapperScriptException.DocumentName = documentName; + wrapperScriptException.LineNumber = lineNumber; + wrapperScriptException.ColumnNumber = columnNumber; + wrapperScriptException.SourceFragment = sourceFragment; + + wrapperException = wrapperScriptException; } - else if (jsException is JsUsageException) + else { - category = "Usage error"; + if (originalException is OriginalUsageException) + { + wrapperException = new WrapperUsageException(message, _engineModeName, originalException); + } + else if (originalException is OriginalEngineException) + { + wrapperException = new WrapperEngineException(message, _engineModeName, originalException); + } + else if (originalException is OriginalFatalException) + { + wrapperException = new WrapperFatalException(message, _engineModeName, originalException); + } + else + { + wrapperException = new WrapperException(message, _engineModeName, originalException); + } } - else if (jsException is JsEngineException) + + wrapperException.Description = description; + + return wrapperException; + } + + private WrapperEngineLoadException WrapDllNotFoundException( + DllNotFoundException originalDllNotFoundException) + { + string originalMessage = originalDllNotFoundException.Message; + string description; + string message; + + if (originalMessage.ContainsQuotedValue(DllName.Chakra)) { - category = "Engine error"; + description = string.Format(CommonStrings.Engine_AssemblyNotRegistered, DllName.Chakra) + " " + + CommonStrings.Engine_EdgeInstallationRequired; + message = JsErrorHelpers.GenerateEngineLoadErrorMessage(description, _engineModeName); } - else if (jsException is JsFatalException) + else { - category = "Fatal error"; + description = originalMessage; + message = JsErrorHelpers.GenerateEngineLoadErrorMessage(description, _engineModeName, true); } - var jsEngineException = new JsRuntimeException(message, _engineModeName) + var wrapperEngineLoadException = new WrapperEngineLoadException(message, _engineModeName, + originalDllNotFoundException) { - ErrorCode = ((uint)jsException.ErrorCode).ToString(CultureInfo.InvariantCulture), - Category = category, - LineNumber = lineNumber, - ColumnNumber = columnNumber, - SourceFragment = sourceFragment, - HelpLink = jsException.HelpLink + Description = description }; - return jsEngineException; + return wrapperEngineLoadException; } - protected override void InnerStartDebugging() + #endregion + + #region ChakraJsRtJsEngineBase overrides + + #region IInnerJsEngine implementation + + public override bool SupportsScriptPrecompilation { - EdgeJsContext.StartDebugging(); + get { return true; } } - private void InvokeScript(Action action) + + public override PrecompiledScript Precompile(string code, string documentName) { - lock (_runSynchronizer) - using (new EdgeJsScope(_jsContext)) + PrecompiledScript precompiledScript = _dispatcher.Invoke(() => { - if (_enableDebugging) + using (new EdgeJsScope(_jsContext)) { - StartDebugging(); - } + try + { + byte[] cachedBytes = EdgeJsContext.SerializeScript(code); - try - { - action(); - } - catch (JsException e) - { - throw ConvertJsExceptionToJsRuntimeException(e); + return new PrecompiledScript(_engineModeName, code, cachedBytes, documentName); + } + catch (OriginalException e) + { + throw WrapJsException(e, documentName); + } } - } + }); + + return precompiledScript; } - private T InvokeScript(Func func) + public override object Evaluate(string expression, string documentName) { - lock (_runSynchronizer) - using (new EdgeJsScope(_jsContext)) + object result = _dispatcher.Invoke(() => { - if (_enableDebugging) + using (new EdgeJsScope(_jsContext)) { - StartDebugging(); - } + try + { + EdgeJsValue resultValue = EdgeJsContext.RunScript(expression, _jsSourceContext++, + documentName); - try - { - return func(); - } - catch (JsException e) - { - throw ConvertJsExceptionToJsRuntimeException(e); + return _typeMapper.MapToHostType(resultValue); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } } - } + }); + + return result; } - /// - /// Destroys object - /// - /// Flag, allowing destruction of - /// managed objects contained in fields of class - private void Dispose(bool disposing) + public override void Execute(string code, string documentName) { - lock (_runSynchronizer) + _dispatcher.Invoke(() => { - if (_disposedFlag.Set()) + using (new EdgeJsScope(_jsContext)) { - _jsRuntime.Dispose(); + try + { + EdgeJsContext.RunScript(code, _jsSourceContext++, documentName); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } } - } - } - - #region IInnerJsEngine implementation - - public override string Mode - { - get { return _engineModeName; } + }); } - public override object Evaluate(string expression) + public override void Execute(PrecompiledScript precompiledScript) { - object result = InvokeScript(() => + _dispatcher.Invoke(() => { - EdgeJsValue resultValue = EdgeJsContext.RunScript(expression); - - return MapToHostType(resultValue); + using (new EdgeJsScope(_jsContext)) + { + try + { + EdgeJsContext.RunSerializedScript(precompiledScript.Code, precompiledScript.CachedBytes, + _jsSourceContext++, precompiledScript.DocumentName); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + finally + { + GC.KeepAlive(precompiledScript); + } + } }); - - return result; - } - - public override void Execute(string code) - { - InvokeScript(() => EdgeJsContext.RunScript(code)); } public override object CallFunction(string functionName, params object[] args) { - object result = InvokeScript(() => + object result = _dispatcher.Invoke(() => { - EdgeJsValue globalObj = EdgeJsValue.GlobalObject; - EdgeJsPropertyId functionId = EdgeJsPropertyId.FromString(functionName); - - bool functionExist = globalObj.HasProperty(functionId); - if (!functionExist) + using (new EdgeJsScope(_jsContext)) { - throw new JsRuntimeException( - string.Format(Strings.Runtime_FunctionNotExist, functionName)); + try + { + EdgeJsValue globalObj = EdgeJsValue.GlobalObject; + EdgeJsPropertyId functionId = EdgeJsPropertyId.FromString(functionName); + + bool functionExist = globalObj.HasProperty(functionId); + if (!functionExist) + { + throw new WrapperRuntimeException( + string.Format(CommonStrings.Runtime_FunctionNotExist, functionName), + _engineModeName + ); + } + + EdgeJsValue resultValue; + EdgeJsValue functionValue = globalObj.GetProperty(functionId); + + int argCount = args.Length; + if (argCount > 0) + { + int processedArgCount = argCount + 1; + var processedArgs = new EdgeJsValue[processedArgCount]; + processedArgs[0] = globalObj; + + for (int argIndex = 0; argIndex < argCount; argIndex++) + { + EdgeJsValue processedArg = _typeMapper.MapToScriptType(args[argIndex]); + AddReferenceToValue(processedArg); + + processedArgs[argIndex + 1] = processedArg; + } + + try + { + resultValue = functionValue.CallFunction(processedArgs); + } + finally + { + for (int argIndex = 1; argIndex < processedArgCount; argIndex++) + { + RemoveReferenceToValue(processedArgs[argIndex]); + } + } + } + else + { + resultValue = functionValue.CallFunction(globalObj); + } + + return _typeMapper.MapToHostType(resultValue); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } } - - var processedArgs = MapToScriptType(args); - var allProcessedArgs = new[] { globalObj }.Concat(processedArgs).ToArray(); - - EdgeJsValue functionValue = globalObj.GetProperty(functionId); - EdgeJsValue resultValue = functionValue.CallFunction(allProcessedArgs); - - return MapToHostType(resultValue); }); return result; @@ -438,19 +603,29 @@ public override object CallFunction(string functionName, params object[] args) public override bool HasVariable(string variableName) { - bool result = InvokeScript(() => + bool result = _dispatcher.Invoke(() => { - EdgeJsValue globalObj = EdgeJsValue.GlobalObject; - EdgeJsPropertyId variableId = EdgeJsPropertyId.FromString(variableName); - bool variableExist = globalObj.HasProperty(variableId); - - if (variableExist) + using (new EdgeJsScope(_jsContext)) { - EdgeJsValue variableValue = globalObj.GetProperty(variableId); - variableExist = (variableValue.ValueType != JsValueType.Undefined); - } + try + { + EdgeJsValue globalObj = EdgeJsValue.GlobalObject; + EdgeJsPropertyId variableId = EdgeJsPropertyId.FromString(variableName); + bool variableExist = globalObj.HasProperty(variableId); + + if (variableExist) + { + EdgeJsValue variableValue = globalObj.GetProperty(variableId); + variableExist = variableValue.ValueType != JsValueType.Undefined; + } - return variableExist; + return variableExist; + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } }); return result; @@ -458,12 +633,21 @@ public override bool HasVariable(string variableName) public override object GetVariableValue(string variableName) { - object result = InvokeScript(() => + object result = _dispatcher.Invoke(() => { - EdgeJsPropertyId variableId = EdgeJsPropertyId.FromString(variableName); - EdgeJsValue variableValue = EdgeJsValue.GlobalObject.GetProperty(variableId); + using (new EdgeJsScope(_jsContext)) + { + try + { + EdgeJsValue variableValue = EdgeJsValue.GlobalObject.GetProperty(variableName); - return MapToHostType(variableValue); + return _typeMapper.MapToHostType(variableValue); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } }); return result; @@ -471,51 +655,104 @@ public override object GetVariableValue(string variableName) public override void SetVariableValue(string variableName, object value) { - InvokeScript(() => + _dispatcher.Invoke(() => { - EdgeJsPropertyId variableId = EdgeJsPropertyId.FromString(variableName); - EdgeJsValue inputValue = MapToScriptType(value); - - EdgeJsValue.GlobalObject.SetProperty(variableId, inputValue, true); + using (new EdgeJsScope(_jsContext)) + { + try + { + EdgeJsValue inputValue = _typeMapper.MapToScriptType(value); + AddReferenceToValue(inputValue); + + try + { + EdgeJsValue.GlobalObject.SetProperty(variableName, inputValue, true); + } + finally + { + RemoveReferenceToValue(inputValue); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } }); } public override void RemoveVariable(string variableName) { - InvokeScript(() => + _dispatcher.Invoke(() => { - EdgeJsValue globalObj = EdgeJsValue.GlobalObject; - EdgeJsPropertyId variableId = EdgeJsPropertyId.FromString(variableName); - - if (globalObj.HasProperty(variableId)) + using (new EdgeJsScope(_jsContext)) { - globalObj.SetProperty(variableId, EdgeJsValue.Undefined, true); + try + { + EdgeJsValue globalObj = EdgeJsValue.GlobalObject; + EdgeJsPropertyId variableId = EdgeJsPropertyId.FromString(variableName); + + if (globalObj.HasProperty(variableId)) + { + globalObj.SetProperty(variableId, EdgeJsValue.Undefined, true); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } } }); } public override void EmbedHostObject(string itemName, object value) { - InvokeScript(() => + _dispatcher.Invoke(() => { - EdgeJsValue processedValue = MapToScriptType(value); - EdgeJsPropertyId itemId = EdgeJsPropertyId.FromString(itemName); - - EdgeJsValue.GlobalObject.SetProperty(itemId, processedValue, true); + using (new EdgeJsScope(_jsContext)) + { + try + { + EdgeJsValue processedValue = _typeMapper.GetOrCreateScriptObject(value); + EdgeJsValue.GlobalObject.SetProperty(itemName, processedValue, true); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } }); } public override void EmbedHostType(string itemName, Type type) { - InvokeScript(() => + _dispatcher.Invoke(() => { - EdgeJsValue typeValue = EdgeJsValue.FromObject(new HostType(type, _engineMode)); - EdgeJsPropertyId itemId = EdgeJsPropertyId.FromString(itemName); - - EdgeJsValue.GlobalObject.SetProperty(itemId, typeValue, true); + using (new EdgeJsScope(_jsContext)) + { + try + { + EdgeJsValue typeValue = _typeMapper.GetOrCreateScriptType(type); + EdgeJsValue.GlobalObject.SetProperty(itemName, typeValue, true); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } }); } + public override void Interrupt() + { + _jsRuntime.Disabled = true; + } + + public override void CollectGarbage() + { + _jsRuntime.CollectGarbage(); + } + #endregion #region IDisposable implementation @@ -529,6 +766,48 @@ 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 is not null) + { + _dispatcher.Invoke(DisposeUnmanagedResources); + + _dispatcher.Dispose(); + _dispatcher = null; + } + + if (_typeMapper is not null) + { + _typeMapper.Dispose(); + _typeMapper = 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/MsieJavaScriptEngine/JsRt/Edge/EdgeJsContext.cs b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsContext.cs index 45e4c9b..43027ea 100644 --- a/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsContext.cs +++ b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsContext.cs @@ -1,9 +1,7 @@ -namespace MsieJavaScriptEngine.JsRt.Edge -{ - using System; - - using JsRt; +using System; +namespace MsieJavaScriptEngine.JsRt.Edge +{ /// /// “Edge” script context /// @@ -81,23 +79,6 @@ public static bool HasException } } - /// - /// Gets a value indicating whether the heap of the current context is being enumerated - /// - /// - /// Requires an active script context. - /// - public static bool IsEnumeratingHeap - { - get - { - bool isEnumerating; - EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsIsEnumeratingHeap(out isEnumerating)); - - return isEnumerating; - } - } - /// /// Gets a runtime that the context belongs to /// @@ -149,10 +130,8 @@ internal EdgeJsContext(IntPtr reference) /// Requires an active script context. /// /// - /// - /// The next system tick when there will be more idle work to do. Returns the - /// maximum number of ticks if there no upcoming idle work to do. - /// + /// The next system tick when there will be more idle work to do. Returns the + /// maximum number of ticks if there no upcoming idle work to do. public static uint Idle() { uint ticks; @@ -162,71 +141,53 @@ 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 + /// A 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 + /// A function representing the script code public static EdgeJsValue ParseScript(string script, JsSourceContext sourceContext, string sourceName) { EdgeJsValue result; - EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsParseScript(script, sourceContext, sourceName, out result)); + EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsParseScript(script, sourceContext, sourceName, + out result)); 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 - /// The cookie identifying the script that can be used + /// A 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 EdgeJsValue ParseScript(string script, byte[] buffer, JsSourceContext sourceContext, string sourceName) + /// A function representing the script code + public static EdgeJsValue ParseSerializedScript(string script, byte[] buffer, JsSourceContext sourceContext, + string sourceName) { EdgeJsValue result; - EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsParseSerializedScript(script, buffer, sourceContext, sourceName, out result)); + EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsParseSerializedScript(script, buffer, sourceContext, + sourceName, out result)); return result; } - /// - /// 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 EdgeJsValue ParseScript(string script) - { - return ParseScript(script, JsSourceContext.None, string.Empty); - } - - /// - /// 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 EdgeJsValue ParseScript(string script, byte[] buffer) - { - return ParseScript(script, buffer, JsSourceContext.None, string.Empty); - } - /// /// Executes a script /// @@ -234,14 +195,15 @@ public static EdgeJsValue ParseScript(string script, byte[] buffer) /// Requires an active script context. /// /// The script to run - /// The cookie identifying the script that can be used + /// A 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 EdgeJsValue RunScript(string script, JsSourceContext sourceContext, string sourceName) { EdgeJsValue result; - EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsRunScript(script, sourceContext, sourceName, out result)); + EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsRunScript(script, sourceContext, sourceName, + out result)); return result; } @@ -250,55 +212,36 @@ public static EdgeJsValue RunScript(string script, JsSourceContext sourceContext /// Runs a serialized 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 source code of the serialized script /// The serialized script - /// The cookie identifying the script that can be used + /// A 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 EdgeJsValue RunScript(string script, byte[] buffer, JsSourceContext sourceContext, string sourceName) + public static EdgeJsValue RunSerializedScript(string script, byte[] buffer, JsSourceContext sourceContext, + string sourceName) { EdgeJsValue result; - EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsRunSerializedScript(script, buffer, sourceContext, sourceName, out result)); + EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsRunSerializedScript(script, buffer, sourceContext, + sourceName, out result)); return result; } - /// - /// Executes a script - /// - /// - /// Requires an active script context. - /// - /// The script to run - /// The result of the script, if any - public static EdgeJsValue RunScript(string script) - { - return RunScript(script, JsSourceContext.None, string.Empty); - } - - /// - /// Runs a serialized script - /// - /// - /// Requires an active script context. - /// - /// The source code of the serialized script - /// The serialized script - /// The result of the script, if any - public static EdgeJsValue RunScript(string script, byte[] buffer) - { - return RunScript(script, buffer, JsSourceContext.None, string.Empty); - } - /// /// Serializes a parsed script to a buffer than can be reused /// /// /// - /// 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. /// @@ -307,14 +250,21 @@ public static EdgeJsValue 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) + /// The buffer to put the serialized script into + public static byte[] SerializeScript(string script) { - var bufferSize = (ulong)buffer.Length; - EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsSerializeScript(script, buffer, ref bufferSize)); + byte[] buffer = null; + uint bufferSize = 0; + + JsErrorCode errorCode = EdgeNativeMethods.JsSerializeScript(script, buffer, ref bufferSize); + EdgeJsErrorHelpers.ThrowIfError(errorCode); + + buffer = new byte[(int)bufferSize]; - return bufferSize; + errorCode = EdgeNativeMethods.JsSerializeScript(script, buffer, ref bufferSize); + EdgeJsErrorHelpers.ThrowIfError(errorCode); + + return buffer; } /// @@ -368,63 +318,11 @@ public static void StartDebugging() EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsStartDebugging()); } - /// - /// Starts profiling in the current context - /// - /// - /// Requires an active script context. - /// - /// The profiling callback to use - /// The profiling events to callback with - /// A context to pass to the profiling callback - public static void StartProfiling(IActiveScriptProfilerCallback callback, ProfilerEventMask eventMask, int context) - { - EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsStartProfiling(callback, eventMask, context)); - } - - /// - /// Stops profiling in the current context - /// - /// - /// - /// Will not return an error if profiling has not started. - /// - /// - /// Requires an active script context. - /// - /// - /// The reason for stopping profiling to pass to the profiler callback - public static void StopProfiling(int reason) - { - EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsStopProfiling(reason)); - } - - /// - /// Enumerates a heap of the current context. - /// - /// - /// - /// While the heap is being enumerated, the current context cannot be removed, and all calls to - /// modify the state of the context will fail until the heap enumerator is released. - /// - /// - /// Requires an active script context. - /// - /// - /// A heap enumerator - public static IActiveScriptProfilerHeapEnum EnumerateHeap() - { - IActiveScriptProfilerHeapEnum enumerator; - EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsEnumerateHeap(out enumerator)); - - return enumerator; - } - /// /// 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() @@ -439,7 +337,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/MsieJavaScriptEngine/JsRt/Edge/EdgeJsErrorHelpers.cs b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsErrorHelpers.cs index df5a44a..755eaaa 100644 --- a/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsErrorHelpers.cs +++ b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsErrorHelpers.cs @@ -8,109 +8,213 @@ internal static class EdgeJsErrorHelpers /// /// 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.OutOfMemory: - throw new JsEngineException(error, "Out of memory."); + #endregion - case JsErrorCode.ScriptException: - { - EdgeJsValue errorObject; - JsErrorCode innerError = EdgeNativeMethods.JsGetAndClearException(out errorObject); + #region Engine - if (innerError != JsErrorCode.NoError) - { - throw new JsFatalException(innerError); - } + case JsErrorCode.OutOfMemory: + throw new JsEngineException(errorCode, "Out of memory."); - throw new EdgeJsScriptException(error, errorObject, "Script threw an exception."); - } + #endregion + + #region Script + case JsErrorCode.ScriptException: case JsErrorCode.ScriptCompile: { EdgeJsValue errorObject; - JsErrorCode innerError = EdgeNativeMethods.JsGetAndClearException(out errorObject); + JsErrorCode innerErrorCode = EdgeNativeMethods.JsGetAndClearException(out errorObject); - if (innerError != JsErrorCode.NoError) + if (innerErrorCode != JsErrorCode.NoError) { - throw new JsFatalException(innerError); + throw new JsFatalException(innerErrorCode); } - throw new EdgeJsScriptException(error, errorObject, "Compile error."); + string message = errorCode == JsErrorCode.ScriptCompile ? + "Compile error." : "Script threw an exception."; + + throw new EdgeJsScriptException(errorCode, errorObject, message); } case JsErrorCode.ScriptTerminated: - throw new EdgeJsScriptException(error, EdgeJsValue.Invalid, "Script was terminated."); + throw new EdgeJsScriptException(errorCode, EdgeJsValue.Invalid, "Script was terminated."); case JsErrorCode.ScriptEvalDisabled: - throw new EdgeJsScriptException(error, EdgeJsValue.Invalid, "Eval of strings is disabled in this runtime."); + throw new EdgeJsScriptException(errorCode, EdgeJsValue.Invalid, "Eval of strings is disabled in this runtime."); + + #endregion + + #region Fatal case JsErrorCode.Fatal: - throw new JsFatalException(error); + throw new JsFatalException(errorCode); + + #endregion default: - throw new JsFatalException(error); + throw new JsFatalException(errorCode); } } } + + + /// + /// Creates a new JavaScript Error object + /// + /// + /// Requires an active script context. + /// + /// The message that describes the error + /// The new error object + public static EdgeJsValue CreateError(string message) + { + EdgeJsValue messageValue = EdgeJsValue.FromString(message); + EdgeJsValue errorValue = EdgeJsValue.CreateError(messageValue); + + return errorValue; + } + + /// + /// Creates a new JavaScript RangeError error object + /// + /// + /// Requires an active script context. + /// + /// The message that describes the error + /// The new error object + public static EdgeJsValue CreateRangeError(string message) + { + EdgeJsValue messageValue = EdgeJsValue.FromString(message); + EdgeJsValue errorValue = EdgeJsValue.CreateRangeError(messageValue); + + return errorValue; + } + + /// + /// Creates a new JavaScript ReferenceError error object + /// + /// + /// Requires an active script context. + /// + /// The message that describes the error + /// The new error object + public static EdgeJsValue CreateReferenceError(string message) + { + EdgeJsValue messageValue = EdgeJsValue.FromString(message); + EdgeJsValue errorValue = EdgeJsValue.CreateReferenceError(messageValue); + + return errorValue; + } + + /// + /// Creates a new JavaScript SyntaxError error object + /// + /// + /// Requires an active script context. + /// + /// The message that describes the error + /// The new error object + public static EdgeJsValue CreateSyntaxError(string message) + { + EdgeJsValue messageValue = EdgeJsValue.FromString(message); + EdgeJsValue errorValue = EdgeJsValue.CreateSyntaxError(messageValue); + + return errorValue; + } + + /// + /// Creates a new JavaScript TypeError error object + /// + /// + /// Requires an active script context. + /// + /// The message that describes the error + /// The new error object + public static EdgeJsValue CreateTypeError(string message) + { + EdgeJsValue messageValue = EdgeJsValue.FromString(message); + EdgeJsValue errorValue = EdgeJsValue.CreateTypeError(messageValue); + + return errorValue; + } + + /// + /// Creates a new JavaScript URIError error object + /// + /// + /// Requires an active script context. + /// + /// The message that describes the error + /// The new error object + public static EdgeJsValue CreateUriError(string message) + { + EdgeJsValue messageValue = EdgeJsValue.FromString(message); + EdgeJsValue errorValue = EdgeJsValue.CreateUriError(messageValue); + + return errorValue; + } } } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsNativeFunction.cs b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsNativeFunction.cs index 3046b57..30f79a2 100644 --- a/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsNativeFunction.cs +++ b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsNativeFunction.cs @@ -1,13 +1,13 @@ -namespace MsieJavaScriptEngine.JsRt.Edge -{ - using System; - using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; +namespace MsieJavaScriptEngine.JsRt.Edge +{ /// /// “Edge” 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/MsieJavaScriptEngine/JsRt/Edge/EdgeJsPropertyId.cs b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsPropertyId.cs index cfcbc1e..3af5b25 100644 --- a/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsPropertyId.cs +++ b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsPropertyId.cs @@ -1,8 +1,8 @@ -namespace MsieJavaScriptEngine.JsRt.Edge -{ - using System; - using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; +namespace MsieJavaScriptEngine.JsRt.Edge +{ /// /// “Edge” property identifier /// @@ -29,9 +29,7 @@ public static EdgeJsPropertyId Invalid /// Gets a name associated with the property ID /// /// - /// /// Requires an active script context. - /// /// public string Name { @@ -40,7 +38,7 @@ public string Name IntPtr buffer; EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsGetPropertyNameFromId(this, out buffer)); - return Marshal.PtrToStringAuto(buffer); + return Marshal.PtrToStringUni(buffer); } } diff --git a/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsRuntime.cs b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsRuntime.cs index e1e80b2..e5f4b20 100644 --- a/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsRuntime.cs +++ b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsRuntime.cs @@ -1,9 +1,7 @@ -namespace MsieJavaScriptEngine.JsRt.Edge -{ - using System; - - using JsRt; +using System; +namespace MsieJavaScriptEngine.JsRt.Edge +{ /// /// “Edge” Chakra runtime /// @@ -17,9 +15,9 @@ /// time. /// /// - /// NOTE: A , unlike other objects in the Chakra hosting API, is not + /// NOTE: A , 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 EdgeJsRuntime : IDisposable @@ -103,15 +101,10 @@ public bool Disabled /// /// 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 EdgeJsRuntime Create(JsRuntimeAttributes attributes, JsThreadServiceCallback threadServiceCallback) + public static EdgeJsRuntime Create() { - EdgeJsRuntime handle; - EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsCreateRuntime(attributes, threadServiceCallback, out handle)); - - return handle; + return Create(JsRuntimeAttributes.None, null); } /// @@ -127,10 +120,15 @@ public static EdgeJsRuntime Create(JsRuntimeAttributes attributes) /// /// 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 EdgeJsRuntime Create() + public static EdgeJsRuntime Create(JsRuntimeAttributes attributes, JsThreadServiceCallback threadServiceCallback) { - return Create(JsRuntimeAttributes.None, null); + EdgeJsRuntime handle; + EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.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/MsieJavaScriptEngine/JsRt/Edge/EdgeJsScope.cs b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsScope.cs index 16c9128..13bbea0 100644 --- a/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsScope.cs +++ b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsScope.cs @@ -1,7 +1,7 @@ -namespace MsieJavaScriptEngine.JsRt.Edge -{ - using System; +using System; +namespace MsieJavaScriptEngine.JsRt.Edge +{ /// /// “Edge” scope automatically sets a context to current and resets the original context /// when disposed @@ -16,7 +16,7 @@ internal struct EdgeJsScope : IDisposable /// /// Whether the structure has been disposed /// - private StatedFlag _disposedFlag; + private bool _disposed; /// @@ -25,7 +25,7 @@ internal struct EdgeJsScope : IDisposable /// The context to create the scope for public EdgeJsScope(EdgeJsContext context) { - _disposedFlag = new StatedFlag(); + _disposed = false; _previousContext = EdgeJsContext.Current; EdgeJsContext.Current = context; } @@ -33,14 +33,17 @@ public EdgeJsScope(EdgeJsContext context) #region IDisposable implementation /// - /// Disposes a scope and sets the previous context to current + /// Disposes the scope and sets the previous context to current /// public void Dispose() { - if (_disposedFlag.Set()) + if (_disposed) { - EdgeJsContext.Current = _previousContext; + return; } + + EdgeJsContext.Current = _previousContext; + _disposed = true; } #endregion diff --git a/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsScriptException.cs b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsScriptException.cs index 14c8119..373c6c6 100644 --- a/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsScriptException.cs +++ b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsScriptException.cs @@ -1,26 +1,30 @@ +#if !NETSTANDARD1_3 +using System; +using System.Runtime.Serialization; + +#endif namespace MsieJavaScriptEngine.JsRt.Edge { - using System; - using System.Runtime.Serialization; - - using JsRt; - /// /// Edge script exception /// +#if !NETSTANDARD1_3 [Serializable] - internal sealed class EdgeJsScriptException : JsException +#endif + public sealed class EdgeJsScriptException : JsException { /// /// The error /// +#if !NETSTANDARD1_3 [NonSerialized] +#endif private readonly EdgeJsValue _error; /// /// Gets a JavaScript object representing the script error /// - public EdgeJsValue Error + internal EdgeJsValue Error { get { return _error; } } @@ -29,31 +33,45 @@ public EdgeJsValue Error /// /// Initializes a new instance of the class /// - /// The error code returned - /// The JavaScript error object - public EdgeJsScriptException(JsErrorCode code, EdgeJsValue error) - : this(code, error, "JavaScript Exception") + /// The error code returned + public EdgeJsScriptException(JsErrorCode errorCode) + : this(errorCode, "JavaScript Exception") { } /// /// Initializes a new instance of the class /// - /// The error code returned + /// The error code returned + /// The error message + public EdgeJsScriptException(JsErrorCode errorCode, string message) + : this(errorCode, EdgeJsValue.Invalid, message) + { } + + /// + /// Initializes a new instance of the class + /// with a specified error message + /// + /// The error code returned /// The JavaScript error object /// The error message - public EdgeJsScriptException(JsErrorCode code, EdgeJsValue error, string message) - : base(code, message) + internal EdgeJsScriptException(JsErrorCode errorCode, EdgeJsValue error, string message) + : base(errorCode, message) { _error = error; } +#if !NETSTANDARD1_3 /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class with serialized data /// - /// The serialization info - /// The streaming context + /// The object that holds the serialized data + /// The contextual information about the source or destination +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif private EdgeJsScriptException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsValue.cs b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsValue.cs index 5a8a69e..140fa09 100644 --- a/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsValue.cs +++ b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsValue.cs @@ -1,16 +1,14 @@ -namespace MsieJavaScriptEngine.JsRt.Edge -{ - using System; - using System.Runtime.InteropServices; - - using JsRt; +using System; +using System.Runtime.InteropServices; +namespace MsieJavaScriptEngine.JsRt.Edge +{ /// /// “Edge” 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 EdgeJsValue { @@ -307,22 +305,6 @@ public static EdgeJsValue FromString(string value) return reference; } - /// - /// Creates a JavaScript value that is a projection of the passed in object - /// - /// - /// Requires an active script context. - /// - /// The object to be projected - /// The JavaScript value that is a projection of the object - public static EdgeJsValue FromObject(object value) - { - EdgeJsValue reference; - EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsVariantToValue(ref value, out reference)); - - return reference; - } - /// /// Creates a new Object /// @@ -344,10 +326,10 @@ public static EdgeJsValue 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 EdgeJsValue CreateExternalObject(IntPtr data, JsObjectFinalizeCallback finalizer) + public static EdgeJsValue CreateExternalObject(IntPtr data, JsFinalizeCallback finalizer) { EdgeJsValue reference; EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsCreateExternalObject(data, finalizer, out reference)); @@ -405,7 +387,7 @@ public static EdgeJsValue CreateArray(uint length) } /// - /// Creates a new JavaScript error object + /// Creates a new JavaScript Error object /// /// /// Requires an active script context. @@ -421,7 +403,7 @@ public static EdgeJsValue CreateError(EdgeJsValue message) } /// - /// Creates a new JavaScript RangeError error object + /// Creates a new JavaScript RangeError error object /// /// /// Requires an active script context. @@ -437,7 +419,7 @@ public static EdgeJsValue CreateRangeError(EdgeJsValue message) } /// - /// Creates a new JavaScript ReferenceError error object + /// Creates a new JavaScript ReferenceError error object /// /// /// Requires an active script context. @@ -453,7 +435,7 @@ public static EdgeJsValue CreateReferenceError(EdgeJsValue message) } /// - /// Creates a new JavaScript SyntaxError error object + /// Creates a new JavaScript SyntaxError error object /// /// /// Requires an active script context. @@ -469,7 +451,7 @@ public static EdgeJsValue CreateSyntaxError(EdgeJsValue message) } /// - /// Creates a new JavaScript TypeError error object + /// Creates a new JavaScript TypeError error object /// /// /// Requires an active script context. @@ -485,7 +467,7 @@ public static EdgeJsValue CreateTypeError(EdgeJsValue message) } /// - /// Creates a new JavaScript URIError error object + /// Creates a new JavaScript URIError error object /// /// /// Requires an active script context. @@ -505,8 +487,8 @@ public static EdgeJsValue CreateUriError(EdgeJsValue 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() @@ -521,7 +503,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() @@ -552,7 +534,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. /// /// @@ -569,41 +551,47 @@ public double ToDouble() } /// - /// Retrieves a string pointer of a String value + /// Retrieves a int value of a Number value /// /// /// - /// This function retrieves the string pointer of a String value. It will fail with - /// InvalidArgument if the type of the value is not String. + /// 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 string - public new string ToString() + /// The int value + public int ToInt32() { - IntPtr buffer; - UIntPtr length; - - EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsStringToPointer(this, out buffer, out length)); + int value; + EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsNumberToInt(this, out value)); - return Marshal.PtrToStringAuto(buffer, (int)length); + return value; } /// - /// Retrieves a object representation of an Object value + /// Retrieves a string pointer of a String value /// /// + /// + /// This function retrieves the string pointer of a String value. It will fail with + /// InvalidArgument if the type of the value is not String. + /// + /// /// Requires an active script context. + /// /// - /// The object representation of the value - public object ToObject() + /// The string + public new string ToString() { - object value; - EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsValueToVariant(this, out value)); + IntPtr buffer; + UIntPtr length; - return value; + EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsStringToPointer(this, out buffer, out length)); + + return Marshal.PtrToStringUni(buffer, (int)length); } /// @@ -850,7 +838,7 @@ public void DeleteIndexedProperty(EdgeJsValue index) /// /// /// - /// This function is equivalent to the "==" operator in JavaScript. + /// This function is equivalent to the == operator in JavaScript. /// /// /// Requires an active script context. @@ -871,7 +859,7 @@ public bool Equals(EdgeJsValue other) /// /// /// - /// This function is equivalent to the "===" operator in JavaScript. + /// This function is equivalent to the === operator in JavaScript. /// /// /// Requires an active script context. @@ -894,14 +882,14 @@ public bool StrictEquals(EdgeJsValue 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 EdgeJsValue CallFunction(params EdgeJsValue[] arguments) { EdgeJsValue returnReference; if (arguments.Length > ushort.MaxValue) { - throw new ArgumentOutOfRangeException("arguments"); + throw new ArgumentOutOfRangeException(nameof(arguments)); } EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsCallFunction(this, arguments, (ushort)arguments.Length, out returnReference)); @@ -916,14 +904,14 @@ public EdgeJsValue CallFunction(params EdgeJsValue[] 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 EdgeJsValue ConstructObject(params EdgeJsValue[] arguments) { EdgeJsValue returnReference; if (arguments.Length > ushort.MaxValue) { - throw new ArgumentOutOfRangeException("arguments"); + throw new ArgumentOutOfRangeException(nameof(arguments)); } EdgeJsErrorHelpers.ThrowIfError(EdgeNativeMethods.JsConstructObject(this, arguments, (ushort)arguments.Length, out returnReference)); diff --git a/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsValueExtensions.cs b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsValueExtensions.cs new file mode 100644 index 0000000..dde90ba --- /dev/null +++ b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeJsValueExtensions.cs @@ -0,0 +1,111 @@ +namespace MsieJavaScriptEngine.JsRt.Edge +{ + /// + /// Extensions for the “Edge” JavaScript value + /// + internal static class EdgeJsValueExtensions + { + /// + /// Gets a property descriptor for an object's own property + /// + /// + /// Requires an active script context. + /// + /// The JavaScript value + /// The name of the property + /// The property descriptor + public static EdgeJsValue GetOwnPropertyDescriptor(this EdgeJsValue source, string propertyName) + { + EdgeJsPropertyId propertyId = EdgeJsPropertyId.FromString(propertyName); + EdgeJsValue resultValue = source.GetOwnPropertyDescriptor(propertyId); + + return resultValue; + } + + /// + /// Determines whether an object has a property + /// + /// + /// Requires an active script context. + /// + /// The JavaScript value + /// The name of the property + /// Whether the object (or a prototype) has the property + public static bool HasProperty(this EdgeJsValue source, string propertyName) + { + EdgeJsPropertyId propertyId = EdgeJsPropertyId.FromString(propertyName); + bool result = source.HasProperty(propertyId); + + return result; + } + + /// + /// Gets an object's property + /// + /// + /// Requires an active script context. + /// + /// The JavaScript value + /// The name of the property + /// The value of the property + public static EdgeJsValue GetProperty(this EdgeJsValue source, string name) + { + EdgeJsPropertyId id = EdgeJsPropertyId.FromString(name); + EdgeJsValue resultValue = source.GetProperty(id); + + return resultValue; + } + + /// + /// Sets an object's property + /// + /// + /// Requires an active script context. + /// + /// The JavaScript value + /// The name of the property + /// The new value of the property + /// The property set should follow strict mode rules + public static void SetProperty(this EdgeJsValue source, string name, EdgeJsValue value, bool useStrictRules) + { + EdgeJsPropertyId id = EdgeJsPropertyId.FromString(name); + source.SetProperty(id, value, useStrictRules); + } + + /// + /// Deletes an object's property + /// + /// + /// Requires an active script context. + /// + /// The JavaScript value + /// The name of the property + /// The property set should follow strict mode rules + /// Whether the property was deleted + public static EdgeJsValue DeleteProperty(this EdgeJsValue source, string propertyName, bool useStrictRules) + { + EdgeJsPropertyId propertyId = EdgeJsPropertyId.FromString(propertyName); + EdgeJsValue resultValue = source.DeleteProperty(propertyId, useStrictRules); + + return resultValue; + } + + /// + /// Defines a new object's own property from a property descriptor + /// + /// + /// Requires an active script context. + /// + /// The JavaScript value + /// The name of the property + /// The property descriptor + /// Whether the property was defined + public static bool DefineProperty(this EdgeJsValue source, string propertyName, EdgeJsValue propertyDescriptor) + { + EdgeJsPropertyId propertyId = EdgeJsPropertyId.FromString(propertyName); + bool result = source.DefineProperty(propertyId, propertyDescriptor); + + return result; + } + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Edge/EdgeNativeMethods.cs b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeNativeMethods.cs index 6b3b217..0e8dd7a 100644 --- a/src/MsieJavaScriptEngine/JsRt/Edge/EdgeNativeMethods.cs +++ b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeNativeMethods.cs @@ -1,275 +1,284 @@ -namespace MsieJavaScriptEngine.JsRt.Edge -{ - using System; - using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; - using Constants; - using JsRt; +using MsieJavaScriptEngine.Constants; +namespace MsieJavaScriptEngine.JsRt.Edge +{ /// /// “Edge” native methods /// internal static class EdgeNativeMethods { - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsCreateRuntime(JsRuntimeAttributes attributes, JsThreadServiceCallback threadService, out EdgeJsRuntime runtime); + #region Hosting [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsCollectGarbage(EdgeJsRuntime handle); + internal static extern JsErrorCode JsParseScript(string script, JsSourceContext sourceContext, + string sourceUrl, out EdgeJsValue result); [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsDisposeRuntime(EdgeJsRuntime handle); + internal static extern JsErrorCode JsRunScript(string script, JsSourceContext sourceContext, + string sourceUrl, out EdgeJsValue result); [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsGetRuntimeMemoryUsage(EdgeJsRuntime runtime, out UIntPtr memoryUsage); + internal static extern JsErrorCode JsSerializeScript(string script, byte[] buffer, ref uint bufferSize); [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsGetRuntimeMemoryLimit(EdgeJsRuntime runtime, out UIntPtr memoryLimit); + internal static extern JsErrorCode JsParseSerializedScript(string script, byte[] buffer, + JsSourceContext sourceContext, string sourceUrl, out EdgeJsValue result); [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsSetRuntimeMemoryLimit(EdgeJsRuntime runtime, UIntPtr memoryLimit); + internal static extern JsErrorCode JsRunSerializedScript(string script, byte[] buffer, + JsSourceContext sourceContext, string sourceUrl, out EdgeJsValue result); [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsSetRuntimeMemoryAllocationCallback(EdgeJsRuntime runtime, IntPtr callbackState, JsMemoryAllocationCallback allocationCallback); + internal static extern JsErrorCode JsGetPropertyIdFromName(string name, out EdgeJsPropertyId propertyId); + + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsGetPropertyNameFromId(EdgeJsPropertyId propertyId, out IntPtr buffer); [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsSetRuntimeBeforeCollectCallback(EdgeJsRuntime runtime, IntPtr callbackState, JsBeforeCollectCallback beforeCollectCallback); + internal static extern JsErrorCode JsPointerToString(string value, UIntPtr stringLength, + out EdgeJsValue stringValue); + + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsStringToPointer(EdgeJsValue value, out IntPtr stringValue, + out UIntPtr stringLength); + + #endregion + + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsCreateRuntime(JsRuntimeAttributes attributes, + JsThreadServiceCallback threadService, out EdgeJsRuntime runtime); + + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsCollectGarbage(EdgeJsRuntime handle); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode, EntryPoint = "JsAddRef")] + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsDisposeRuntime(EdgeJsRuntime handle); + + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsGetRuntimeMemoryUsage(EdgeJsRuntime runtime, out UIntPtr memoryUsage); + + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsGetRuntimeMemoryLimit(EdgeJsRuntime runtime, out UIntPtr memoryLimit); + + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsSetRuntimeMemoryLimit(EdgeJsRuntime runtime, UIntPtr memoryLimit); + + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsSetRuntimeMemoryAllocationCallback(EdgeJsRuntime runtime, + IntPtr callbackState, JsMemoryAllocationCallback allocationCallback); + + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsSetRuntimeBeforeCollectCallback(EdgeJsRuntime runtime, + IntPtr callbackState, JsBeforeCollectCallback beforeCollectCallback); + + [DllImport(DllName.Chakra, EntryPoint = "JsAddRef")] internal static extern JsErrorCode JsContextAddRef(EdgeJsContext reference, out uint count); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsAddRef(EdgeJsValue reference, out uint count); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode, EntryPoint = "JsRelease")] + [DllImport(DllName.Chakra, EntryPoint = "JsRelease")] internal static extern JsErrorCode JsContextRelease(EdgeJsContext reference, out uint count); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsRelease(EdgeJsValue reference, out uint count); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsCreateContext(EdgeJsRuntime runtime, out EdgeJsContext newContext); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsGetCurrentContext(out EdgeJsContext currentContext); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsSetCurrentContext(EdgeJsContext context); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsGetRuntime(EdgeJsContext context, out EdgeJsRuntime runtime); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsStartDebugging(); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsIdle(out uint nextIdleTick); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsParseScript(string script, JsSourceContext sourceContext, string sourceUrl, out EdgeJsValue result); - - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsRunScript(string script, JsSourceContext sourceContext, string sourceUrl, out EdgeJsValue result); - - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsSerializeScript(string script, byte[] buffer, ref ulong bufferSize); - - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsParseSerializedScript(string script, byte[] buffer, JsSourceContext sourceContext, string sourceUrl, out EdgeJsValue result); - - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsRunSerializedScript(string script, byte[] buffer, JsSourceContext sourceContext, string sourceUrl, out EdgeJsValue result); - - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsGetPropertyIdFromName(string name, out EdgeJsPropertyId propertyId); - - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsGetPropertyNameFromId(EdgeJsPropertyId propertyId, out IntPtr buffer); - - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsGetUndefinedValue(out EdgeJsValue undefinedValue); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsGetNullValue(out EdgeJsValue nullValue); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsGetTrueValue(out EdgeJsValue trueValue); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsGetFalseValue(out EdgeJsValue falseValue); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsBoolToBoolean(bool value, out EdgeJsValue booleanValue); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsBooleanToBool(EdgeJsValue booleanValue, out bool boolValue); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsConvertValueToBoolean(EdgeJsValue value, out EdgeJsValue booleanValue); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsGetValueType(EdgeJsValue value, out JsValueType type); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsDoubleToNumber(double doubleValue, out EdgeJsValue value); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsIntToNumber(int intValue, out EdgeJsValue value); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsNumberToDouble(EdgeJsValue value, out double doubleValue); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsNumberToInt(EdgeJsValue value, out int intValue); + + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsConvertValueToNumber(EdgeJsValue value, out EdgeJsValue numberValue); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsGetStringLength(EdgeJsValue sringValue, out int length); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsPointerToString(string value, UIntPtr stringLength, out EdgeJsValue stringValue); - - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsStringToPointer(EdgeJsValue value, out IntPtr stringValue, out UIntPtr stringLength); - - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsConvertValueToString(EdgeJsValue value, out EdgeJsValue stringValue); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsVariantToValue([MarshalAs(UnmanagedType.Struct)] ref object var, out EdgeJsValue value); - - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsValueToVariant(EdgeJsValue obj, [MarshalAs(UnmanagedType.Struct)] out object var); - - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsGetGlobalObject(out EdgeJsValue globalObject); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsCreateObject(out EdgeJsValue obj); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsCreateExternalObject(IntPtr data, JsObjectFinalizeCallback finalizeCallback, out EdgeJsValue obj); + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsCreateExternalObject(IntPtr data, + JsFinalizeCallback finalizeCallback, out EdgeJsValue obj); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsConvertValueToObject(EdgeJsValue value, out EdgeJsValue obj); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsGetPrototype(EdgeJsValue obj, out EdgeJsValue prototypeObject); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsSetPrototype(EdgeJsValue obj, EdgeJsValue prototypeObject); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsGetExtensionAllowed(EdgeJsValue obj, out bool value); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsPreventExtension(EdgeJsValue obj); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsGetProperty(EdgeJsValue obj, EdgeJsPropertyId propertyId, out EdgeJsValue value); + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsGetProperty(EdgeJsValue obj, EdgeJsPropertyId propertyId, + out EdgeJsValue value); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsGetOwnPropertyDescriptor(EdgeJsValue obj, EdgeJsPropertyId propertyId, out EdgeJsValue propertyDescriptor); + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsGetOwnPropertyDescriptor(EdgeJsValue obj, EdgeJsPropertyId propertyId, + out EdgeJsValue propertyDescriptor); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsGetOwnPropertyNames(EdgeJsValue obj, out EdgeJsValue propertyNames); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsSetProperty(EdgeJsValue obj, EdgeJsPropertyId propertyId, EdgeJsValue value, bool useStrictRules); + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsSetProperty(EdgeJsValue obj, EdgeJsPropertyId propertyId, + EdgeJsValue value, bool useStrictRules); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsHasProperty(EdgeJsValue obj, EdgeJsPropertyId propertyId, out bool hasProperty); + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsHasProperty(EdgeJsValue obj, EdgeJsPropertyId propertyId, + out bool hasProperty); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsDeleteProperty(EdgeJsValue obj, EdgeJsPropertyId propertyId, bool useStrictRules, out EdgeJsValue result); + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsDeleteProperty(EdgeJsValue obj, EdgeJsPropertyId propertyId, + bool useStrictRules, out EdgeJsValue result); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsDefineProperty(EdgeJsValue obj, EdgeJsPropertyId propertyId, EdgeJsValue propertyDescriptor, out bool result); + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsDefineProperty(EdgeJsValue obj, EdgeJsPropertyId propertyId, + EdgeJsValue propertyDescriptor, out bool result); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsHasIndexedProperty(EdgeJsValue obj, EdgeJsValue index, out bool result); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsGetIndexedProperty(EdgeJsValue obj, EdgeJsValue index, out EdgeJsValue result); + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsGetIndexedProperty(EdgeJsValue obj, EdgeJsValue index, + out EdgeJsValue result); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsSetIndexedProperty(EdgeJsValue obj, EdgeJsValue index, EdgeJsValue value); + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsSetIndexedProperty(EdgeJsValue obj, EdgeJsValue index, + EdgeJsValue value); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsDeleteIndexedProperty(EdgeJsValue obj, EdgeJsValue index); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsEquals(EdgeJsValue obj1, EdgeJsValue obj2, out bool result); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsStrictEquals(EdgeJsValue obj1, EdgeJsValue obj2, out bool result); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsHasExternalData(EdgeJsValue obj, out bool value); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsGetExternalData(EdgeJsValue obj, out IntPtr externalData); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsSetExternalData(EdgeJsValue obj, IntPtr externalData); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsCreateArray(uint length, out EdgeJsValue result); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsCallFunction(EdgeJsValue function, EdgeJsValue[] arguments, ushort argumentCount, out EdgeJsValue result); + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsCallFunction(EdgeJsValue function, EdgeJsValue[] arguments, + ushort argumentCount, out EdgeJsValue result); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsConstructObject(EdgeJsValue function, EdgeJsValue[] arguments, ushort argumentCount, out EdgeJsValue result); + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsConstructObject(EdgeJsValue function, EdgeJsValue[] arguments, + ushort argumentCount, out EdgeJsValue result); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsCreateFunction(EdgeJsNativeFunction nativeFunction, IntPtr externalData, out EdgeJsValue function); + [DllImport(DllName.Chakra)] + internal static extern JsErrorCode JsCreateFunction(EdgeJsNativeFunction nativeFunction, + IntPtr externalData, out EdgeJsValue function); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsCreateError(EdgeJsValue message, out EdgeJsValue error); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsCreateRangeError(EdgeJsValue message, out EdgeJsValue error); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsCreateReferenceError(EdgeJsValue message, out EdgeJsValue error); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsCreateSyntaxError(EdgeJsValue message, out EdgeJsValue error); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsCreateTypeError(EdgeJsValue message, out EdgeJsValue error); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsCreateURIError(EdgeJsValue message, out EdgeJsValue error); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsHasException(out bool hasException); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsGetAndClearException(out EdgeJsValue exception); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsSetException(EdgeJsValue exception); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsDisableRuntimeExecution(EdgeJsRuntime runtime); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsEnableRuntimeExecution(EdgeJsRuntime runtime); - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] + [DllImport(DllName.Chakra)] internal static extern JsErrorCode JsIsRuntimeExecutionDisabled(EdgeJsRuntime runtime, out bool isDisabled); - - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsStartProfiling(IActiveScriptProfilerCallback callback, ProfilerEventMask eventMask, int context); - - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsStopProfiling(int reason); - - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsEnumerateHeap(out IActiveScriptProfilerHeapEnum enumerator); - - [DllImport(DllName.Chakra, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsIsEnumeratingHeap(out bool isEnumeratingHeap); } } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Edge/EdgeTypeMapper.cs b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeTypeMapper.cs new file mode 100644 index 0000000..43fc4ac --- /dev/null +++ b/src/MsieJavaScriptEngine/JsRt/Edge/EdgeTypeMapper.cs @@ -0,0 +1,708 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using MsieJavaScriptEngine.Extensions; +using MsieJavaScriptEngine.Helpers; +using MsieJavaScriptEngine.JsRt.Embedding; +using MsieJavaScriptEngine.Resources; + +using WrapperException = MsieJavaScriptEngine.JsException; + +namespace MsieJavaScriptEngine.JsRt.Edge +{ + using EdgeEmbeddedItem = EmbeddedItem; + using EdgeEmbeddedObject = EmbeddedObject; + using EdgeEmbeddedType = EmbeddedType; + + /// + /// “Edge” type mapper + /// + internal sealed class EdgeTypeMapper : TypeMapper + { + /// + /// Constructs an instance of the “Edge” type mapper + /// + /// Flag for whether to allow the usage of reflection API in the script code + public EdgeTypeMapper(bool allowReflection) + : base(allowReflection) + { } + + + /// + /// Makes a mapping of value from the host type to a script type + /// + /// The source value + /// The mapped value + public override EdgeJsValue MapToScriptType(object value) + { + if (value is null) + { + return EdgeJsValue.Null; + } + + if (value is Undefined) + { + return EdgeJsValue.Undefined; + } + + var typeCode = value.GetType().GetTypeCode(); + + switch (typeCode) + { + case TypeCode.Boolean: + return (bool)value ? EdgeJsValue.True : EdgeJsValue.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 EdgeJsValue.FromInt32(Convert.ToInt32(value)); + + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return EdgeJsValue.FromDouble(Convert.ToDouble(value)); + + case TypeCode.Char: + case TypeCode.String: + return EdgeJsValue.FromString((string)value); + + default: + return value is EdgeJsValue ? (EdgeJsValue)value : GetOrCreateScriptObject(value); + } + } + + /// + /// Makes a mapping of value from the script type to a host type + /// + /// The source value + /// The mapped value + public override object MapToHostType(EdgeJsValue 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: + EdgeJsPropertyId externalObjectPropertyId = EdgeJsPropertyId.FromString(ExternalObjectPropertyName); + if (value.HasProperty(externalObjectPropertyId)) + { + EdgeJsValue 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: + result = value.HasExternalData ? + GCHandle.FromIntPtr(value.ExternalData).Target : value.ConvertToObject(); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + return result; + } + + protected override EdgeEmbeddedObject CreateEmbeddedObjectOrFunction(object obj) + { + var del = obj as Delegate; + EdgeEmbeddedObject embeddedObject = del is not null ? + CreateEmbeddedFunction(del) : CreateEmbeddedObject(obj); + + return embeddedObject; + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private EdgeEmbeddedObject CreateEmbeddedObject(object obj) + { + GCHandle objHandle = GCHandle.Alloc(obj); + IntPtr objPtr = GCHandle.ToIntPtr(objHandle); + EdgeJsValue objValue = EdgeJsValue.CreateExternalObject(objPtr, _embeddedObjectFinalizeCallback); + + var embeddedObject = new EdgeEmbeddedObject(obj, objValue); + + ProjectFields(embeddedObject); + ProjectProperties(embeddedObject); + ProjectMethods(embeddedObject); + FreezeObject(objValue); + + return embeddedObject; + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private EdgeEmbeddedObject CreateEmbeddedFunction(Delegate del) + { + EdgeJsNativeFunction 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) + { + EdgeJsValue undefinedValue = EdgeJsValue.Undefined; + Exception exception = UnwrapException(e); + var wrapperException = exception as WrapperException; + EdgeJsValue errorValue = wrapperException is not null ? + CreateErrorFromWrapperException(wrapperException) + : + EdgeJsErrorHelpers.CreateError(string.Format( + CommonStrings.Runtime_HostDelegateInvocationFailed, exception.Message)) + ; + EdgeJsContext.SetException(errorValue); + + return undefinedValue; + } + + EdgeJsValue resultValue = MapToScriptType(result); + + return resultValue; + }; + + GCHandle delHandle = GCHandle.Alloc(del); + IntPtr delPtr = GCHandle.ToIntPtr(delHandle); + EdgeJsValue objValue = EdgeJsValue.CreateExternalObject(delPtr, _embeddedObjectFinalizeCallback); + + EdgeJsValue functionValue = EdgeJsValue.CreateFunction(nativeFunction); + SetNonEnumerableProperty(functionValue, ExternalObjectPropertyName, objValue); + + var embeddedObject = new EdgeEmbeddedObject(del, functionValue, [nativeFunction]); + + return embeddedObject; + } + + protected override EdgeEmbeddedType 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); + + EdgeJsNativeFunction nativeConstructorFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + object result; + EdgeJsValue resultValue; + object[] processedArgs = GetHostItemMemberArguments(args); + + if (processedArgs.Length == 0 && typeInfo.IsValueType) + { + result = Activator.CreateInstance(type); + resultValue = MapToScriptType(result); + + return resultValue; + } + + EdgeJsValue undefinedValue = EdgeJsValue.Undefined; + + if (constructors.Length == 0) + { + CreateAndSetError(string.Format(CommonStrings.Runtime_HostTypeConstructorNotFound, typeName)); + return undefinedValue; + } + + var bestFitConstructor = (ConstructorInfo)ReflectionHelpers.GetBestFitMethod( + constructors, processedArgs); + if (bestFitConstructor is null) + { + CreateAndSetReferenceError(string.Format( + CommonStrings.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; + EdgeJsValue errorValue = wrapperException is not null ? + CreateErrorFromWrapperException(wrapperException) + : + EdgeJsErrorHelpers.CreateError(string.Format( + CommonStrings.Runtime_HostTypeConstructorInvocationFailed, typeName, exception.Message)) + ; + EdgeJsContext.SetException(errorValue); + + return undefinedValue; + } + + resultValue = MapToScriptType(result); + + return resultValue; + }; + + GCHandle embeddedTypeHandle = GCHandle.Alloc(type); + IntPtr embeddedTypePtr = GCHandle.ToIntPtr(embeddedTypeHandle); + EdgeJsValue objValue = EdgeJsValue.CreateExternalObject(embeddedTypePtr, + _embeddedTypeFinalizeCallback); + + EdgeJsValue typeValue = EdgeJsValue.CreateFunction(nativeConstructorFunction); + SetNonEnumerableProperty(typeValue, ExternalObjectPropertyName, objValue); + + var embeddedType = new EdgeEmbeddedType(type, typeValue, + new List { nativeConstructorFunction }); + + ProjectFields(embeddedType); + ProjectProperties(embeddedType); + ProjectMethods(embeddedType); + FreezeObject(typeValue); + + return embeddedType; + } + + private void ProjectFields(EdgeEmbeddedItem externalItem) + { + Type type = externalItem.HostType; + object obj = externalItem.HostObject; + EdgeJsValue 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; + + EdgeJsValue descriptorValue = EdgeJsValue.CreateObject(); + descriptorValue.SetProperty("enumerable", EdgeJsValue.True, true); + + EdgeJsNativeFunction nativeGetFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + EdgeJsValue undefinedValue = EdgeJsValue.Undefined; + + if (instance && obj is null) + { + CreateAndSetTypeError(string.Format( + CommonStrings.Runtime_InvalidThisContextForHostObjectField, fieldName)); + return undefinedValue; + } + + object result; + + try + { + result = field.GetValue(obj); + } + catch (Exception e) + { + Exception exception = UnwrapException(e); + var wrapperException = exception as WrapperException; + EdgeJsValue errorValue; + + if (wrapperException is not null) + { + errorValue = CreateErrorFromWrapperException(wrapperException); + } + else + { + string errorMessage = instance ? + string.Format(CommonStrings.Runtime_HostObjectFieldGettingFailed, fieldName, + exception.Message) + : + string.Format(CommonStrings.Runtime_HostTypeFieldGettingFailed, fieldName, typeName, + exception.Message) + ; + errorValue = EdgeJsErrorHelpers.CreateError(errorMessage); + } + EdgeJsContext.SetException(errorValue); + + return undefinedValue; + } + + EdgeJsValue resultValue = MapToScriptType(result); + + return resultValue; + }; + nativeFunctions.Add(nativeGetFunction); + + EdgeJsValue getMethodValue = EdgeJsValue.CreateFunction(nativeGetFunction); + descriptorValue.SetProperty("get", getMethodValue, true); + + if (!field.IsInitOnly) + { + EdgeJsNativeFunction nativeSetFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + EdgeJsValue undefinedValue = EdgeJsValue.Undefined; + + if (instance && obj is null) + { + CreateAndSetTypeError(string.Format( + CommonStrings.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; + EdgeJsValue errorValue; + + if (wrapperException is not null) + { + errorValue = CreateErrorFromWrapperException(wrapperException); + } + else + { + string errorMessage = instance ? + string.Format(CommonStrings.Runtime_HostObjectFieldSettingFailed, fieldName, + exception.Message) + : + string.Format(CommonStrings.Runtime_HostTypeFieldSettingFailed, fieldName, typeName, + exception.Message) + ; + errorValue = EdgeJsErrorHelpers.CreateError(errorMessage); + } + EdgeJsContext.SetException(errorValue); + + return undefinedValue; + } + + return undefinedValue; + }; + nativeFunctions.Add(nativeSetFunction); + + EdgeJsValue setMethodValue = EdgeJsValue.CreateFunction(nativeSetFunction); + descriptorValue.SetProperty("set", setMethodValue, true); + } + + typeValue.DefineProperty(fieldName, descriptorValue); + } + } + + private void ProjectProperties(EdgeEmbeddedItem externalItem) + { + Type type = externalItem.HostType; + object obj = externalItem.HostObject; + EdgeJsValue 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; + + EdgeJsValue descriptorValue = EdgeJsValue.CreateObject(); + descriptorValue.SetProperty("enumerable", EdgeJsValue.True, true); + + if (property.CanRead) + { + EdgeJsNativeFunction nativeGetFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + EdgeJsValue undefinedValue = EdgeJsValue.Undefined; + + if (instance && obj is null) + { + CreateAndSetTypeError(string.Format( + CommonStrings.Runtime_InvalidThisContextForHostObjectProperty, propertyName)); + return undefinedValue; + } + + object result; + + try + { + result = property.GetValue(obj, []); + } + catch (Exception e) + { + Exception exception = UnwrapException(e); + var wrapperException = exception as WrapperException; + EdgeJsValue errorValue; + + if (wrapperException is not null) + { + errorValue = CreateErrorFromWrapperException(wrapperException); + } + else + { + string errorMessage = instance ? + string.Format(CommonStrings.Runtime_HostObjectPropertyGettingFailed, propertyName, + exception.Message) + : + string.Format(CommonStrings.Runtime_HostTypePropertyGettingFailed, propertyName, + typeName, exception.Message) + ; + errorValue = EdgeJsErrorHelpers.CreateError(errorMessage); + } + EdgeJsContext.SetException(errorValue); + + return undefinedValue; + } + + EdgeJsValue resultValue = MapToScriptType(result); + + return resultValue; + }; + nativeFunctions.Add(nativeGetFunction); + + EdgeJsValue getMethodValue = EdgeJsValue.CreateFunction(nativeGetFunction); + descriptorValue.SetProperty("get", getMethodValue, true); + } + + if (property.CanWrite) + { + EdgeJsNativeFunction nativeSetFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + EdgeJsValue undefinedValue = EdgeJsValue.Undefined; + + if (instance && obj is null) + { + CreateAndSetTypeError(string.Format( + CommonStrings.Runtime_InvalidThisContextForHostObjectProperty, propertyName)); + return undefinedValue; + } + + object value = MapToHostType(args[1]); + ReflectionHelpers.FixPropertyValueType(ref value, property); + + try + { + property.SetValue(obj, value, []); + } + catch (Exception e) + { + Exception exception = UnwrapException(e); + var wrapperException = exception as WrapperException; + EdgeJsValue errorValue; + + if (wrapperException is not null) + { + errorValue = CreateErrorFromWrapperException(wrapperException); + } + else + { + string errorMessage = instance ? + string.Format(CommonStrings.Runtime_HostObjectPropertySettingFailed, propertyName, + exception.Message) + : + string.Format(CommonStrings.Runtime_HostTypePropertySettingFailed, propertyName, + typeName, exception.Message) + ; + errorValue = EdgeJsErrorHelpers.CreateError(errorMessage); + } + EdgeJsContext.SetException(errorValue); + + return undefinedValue; + } + + return undefinedValue; + }; + nativeFunctions.Add(nativeSetFunction); + + EdgeJsValue setMethodValue = EdgeJsValue.CreateFunction(nativeSetFunction); + descriptorValue.SetProperty("set", setMethodValue, true); + } + + typeValue.DefineProperty(propertyName, descriptorValue); + } + } + + private void ProjectMethods(EdgeEmbeddedItem externalItem) + { + Type type = externalItem.HostType; + object obj = externalItem.HostObject; + EdgeJsValue 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(); + + EdgeJsNativeFunction nativeFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + EdgeJsValue undefinedValue = EdgeJsValue.Undefined; + + if (instance && obj is null) + { + CreateAndSetTypeError(string.Format( + CommonStrings.Runtime_InvalidThisContextForHostObjectMethod, methodName)); + return undefinedValue; + } + + object[] processedArgs = GetHostItemMemberArguments(args); + + var bestFitMethod = (MethodInfo)ReflectionHelpers.GetBestFitMethod( + methodCandidates, processedArgs); + if (bestFitMethod is null) + { + CreateAndSetReferenceError(string.Format( + CommonStrings.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; + EdgeJsValue errorValue; + + if (wrapperException is not null) + { + errorValue = CreateErrorFromWrapperException(wrapperException); + } + else + { + string errorMessage = instance ? + string.Format(CommonStrings.Runtime_HostObjectMethodInvocationFailed, methodName, + exception.Message) + : + string.Format(CommonStrings.Runtime_HostTypeMethodInvocationFailed, methodName, typeName, + exception.Message) + ; + errorValue = EdgeJsErrorHelpers.CreateError(errorMessage); + } + EdgeJsContext.SetException(errorValue); + + return undefinedValue; + } + + EdgeJsValue resultValue = MapToScriptType(result); + + return resultValue; + }; + nativeFunctions.Add(nativeFunction); + + EdgeJsValue methodValue = EdgeJsValue.CreateFunction(nativeFunction); + typeValue.SetProperty(methodName, methodValue, true); + } + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static void FreezeObject(EdgeJsValue objValue) + { + EdgeJsValue freezeMethodValue = EdgeJsValue.GlobalObject + .GetProperty("Object") + .GetProperty("freeze") + ; + freezeMethodValue.CallFunction(objValue); + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static void SetNonEnumerableProperty(EdgeJsValue objValue, string name, EdgeJsValue value) + { + EdgeJsValue descriptorValue = EdgeJsValue.CreateObject(); + descriptorValue.SetProperty("enumerable", EdgeJsValue.False, true); + descriptorValue.SetProperty("writable", EdgeJsValue.True, true); + + EdgeJsPropertyId id = EdgeJsPropertyId.FromString(name); + objValue.DefineProperty(id, descriptorValue); + objValue.SetProperty(id, value, true); + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static void CreateAndSetError(string message) + { + EdgeJsValue errorValue = EdgeJsErrorHelpers.CreateError(message); + EdgeJsContext.SetException(errorValue); + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static void CreateAndSetReferenceError(string message) + { + EdgeJsValue errorValue = EdgeJsErrorHelpers.CreateReferenceError(message); + EdgeJsContext.SetException(errorValue); + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static void CreateAndSetTypeError(string message) + { + EdgeJsValue errorValue = EdgeJsErrorHelpers.CreateTypeError(message); + EdgeJsContext.SetException(errorValue); + } + + private static EdgeJsValue CreateErrorFromWrapperException(WrapperException exception) + { + var originalException = (JsException)exception.InnerException; + var originalScriptException = originalException as EdgeJsScriptException; + EdgeJsValue errorValue = originalScriptException is not null ? + originalScriptException.Error + : + EdgeJsErrorHelpers.CreateError(exception.Description) + ; + + return errorValue; + } + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Embedding/EmbeddedItem.cs b/src/MsieJavaScriptEngine/JsRt/Embedding/EmbeddedItem.cs new file mode 100644 index 0000000..52ab74d --- /dev/null +++ b/src/MsieJavaScriptEngine/JsRt/Embedding/EmbeddedItem.cs @@ -0,0 +1,123 @@ +using System; +using System.Collections.Generic; + +using MsieJavaScriptEngine.Utilities; + +namespace MsieJavaScriptEngine.JsRt.Embedding +{ + /// + /// Embedded item + /// + /// The type of the JavaScript value + /// The type of the native function + internal abstract class EmbeddedItem : IDisposable + where TValue : struct + where TFunction : Delegate + { + /// + /// Host type + /// + private Type _hostType; + + /// + /// Instance of host type + /// + private object _hostObject; + + /// + /// JavaScript value created from an host item + /// + private readonly TValue _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 TValue 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, TValue 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 is not null) + { + nativeFunctions.Clear(); + _nativeFunctions = null; + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Embedding/EmbeddedObject.cs b/src/MsieJavaScriptEngine/JsRt/Embedding/EmbeddedObject.cs new file mode 100644 index 0000000..8300a73 --- /dev/null +++ b/src/MsieJavaScriptEngine/JsRt/Embedding/EmbeddedObject.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; + +namespace MsieJavaScriptEngine.JsRt.Embedding +{ + /// + /// Embedded object + /// + /// The type of the JavaScript value + /// The type of the native function + internal sealed class EmbeddedObject : EmbeddedItem + where TValue : struct + where TFunction : Delegate + { + /// + /// Constructs an instance of the embedded object + /// + /// Instance of host type + /// JavaScript value created from an host object + public EmbeddedObject(object hostObject, TValue 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, TValue 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/MsieJavaScriptEngine/JsRt/Embedding/EmbeddedObjectKey.cs b/src/MsieJavaScriptEngine/JsRt/Embedding/EmbeddedObjectKey.cs new file mode 100644 index 0000000..4131629 --- /dev/null +++ b/src/MsieJavaScriptEngine/JsRt/Embedding/EmbeddedObjectKey.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +using MsieJavaScriptEngine.Resources; + +namespace MsieJavaScriptEngine.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 is 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 is null) + { + return 1; + } + + if (!(other is EmbeddedObjectKey)) + { + throw new ArgumentException( + string.Format(CommonStrings.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 is null) + { + return 1; + } + + if (!(other is EmbeddedObjectKey)) + { + throw new ArgumentException( + string.Format(CommonStrings.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/MsieJavaScriptEngine/JsRt/Embedding/EmbeddedType.cs b/src/MsieJavaScriptEngine/JsRt/Embedding/EmbeddedType.cs new file mode 100644 index 0000000..479ce5f --- /dev/null +++ b/src/MsieJavaScriptEngine/JsRt/Embedding/EmbeddedType.cs @@ -0,0 +1,46 @@ +using System; +using System.Collections.Generic; + +namespace MsieJavaScriptEngine.JsRt.Embedding +{ + /// + /// Embedded type + /// + /// The type of the JavaScript value + /// The type of the native function + internal sealed class EmbeddedType : EmbeddedItem + where TValue : struct + where TFunction : Delegate + { + /// + /// Constructs an instance of the embedded type + /// + /// Host type + /// JavaScript value created from an host type + public EmbeddedType(Type hostType, TValue 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, TValue 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/MsieJavaScriptEngine/JsRt/IActiveScriptProfilerCallback.cs b/src/MsieJavaScriptEngine/JsRt/IActiveScriptProfilerCallback.cs deleted file mode 100644 index a6cb347..0000000 --- a/src/MsieJavaScriptEngine/JsRt/IActiveScriptProfilerCallback.cs +++ /dev/null @@ -1,57 +0,0 @@ -namespace MsieJavaScriptEngine.JsRt -{ - using System; - using System.Runtime.InteropServices; - - /// - /// IActiveScriptProfilerCallback COM interface - /// - [Guid("740eca23-7d9d-42e5-ba9d-f8b24b1c7a9b")] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - internal interface IActiveScriptProfilerCallback - { - /// - /// Called when the profile is started - /// - /// The context provided when profiling was started - void Initialize(uint context); - - /// - /// Called when profiling is stopped - /// - /// The reason code provided when profiling was stopped - void Shutdown(uint reason); - - /// - /// Called when a script is compiled - /// - /// The ID of the script - /// The type of the script - /// The debug document context, if any - void ScriptCompiled(int scriptId, ProfilerScriptType type, IntPtr debugDocumentContext); - - /// - /// Called when a function is compiled - /// - /// The ID of the function - /// The ID of the script - /// The name of the function - /// The function name hint - /// The debug document context, if any - void FunctionCompiled(int functionId, int scriptId, [MarshalAs(UnmanagedType.LPWStr)] string functionName, [MarshalAs(UnmanagedType.LPWStr)] string functionNameHint, IntPtr debugDocumentContext); - - /// - /// Called when a function is entered - /// - /// The ID of the script - /// The ID of the function - void OnFunctionEnter(int scriptId, int functionId); - - /// - /// Called when a function is exited - /// - /// The ID of the script - /// The ID of the function - void OnFunctionExit(int scriptId, int functionId); - } -} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/IActiveScriptProfilerCallback2.cs b/src/MsieJavaScriptEngine/JsRt/IActiveScriptProfilerCallback2.cs deleted file mode 100644 index bdb942d..0000000 --- a/src/MsieJavaScriptEngine/JsRt/IActiveScriptProfilerCallback2.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace MsieJavaScriptEngine.JsRt -{ - using System.Runtime.InteropServices; - - /// - /// IActiveScriptProfilerCallback2 COM interface - /// - [Guid("31B7F8AD-A637-409C-B22F-040995B6103D")] - internal interface IActiveScriptProfilerCallback2 : IActiveScriptProfilerCallback - { - /// - /// Called when a function is entered by name - /// - /// The name of the function - /// The type of the function - void OnFunctionEnterByName(string functionName, ProfilerScriptType type); - - /// - /// Called when a function is exited by name - /// - /// The name of the function - /// The type of the function - void OnFunctionExitByName(string functionName, ProfilerScriptType type); - } -} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/IActiveScriptProfilerHeapEnum.cs b/src/MsieJavaScriptEngine/JsRt/IActiveScriptProfilerHeapEnum.cs deleted file mode 100644 index d1d2671..0000000 --- a/src/MsieJavaScriptEngine/JsRt/IActiveScriptProfilerHeapEnum.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace MsieJavaScriptEngine.JsRt -{ - using System.Diagnostics.CodeAnalysis; - using System.Runtime.InteropServices; - - /// - /// IActiveScriptProfilerHeapEnum COM interface - /// - [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Name defined in COM.")] - [Guid("32E4694E-0D37-419B-B93D-FA20DED6E8EA")] - internal interface IActiveScriptProfilerHeapEnum - { } -} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/ChakraIeJsRtJsEngine.cs b/src/MsieJavaScriptEngine/JsRt/Ie/ChakraIeJsRtJsEngine.cs index 9fcc60d..29c337f 100644 --- a/src/MsieJavaScriptEngine/JsRt/Ie/ChakraIeJsRtJsEngine.cs +++ b/src/MsieJavaScriptEngine/JsRt/Ie/ChakraIeJsRtJsEngine.cs @@ -1,15 +1,33 @@ -namespace MsieJavaScriptEngine.JsRt.Ie +using System; +using System.Text; + +using AdvancedStringBuilder; + +using MsieJavaScriptEngine.Constants; +using MsieJavaScriptEngine.Extensions; +using MsieJavaScriptEngine.Helpers; +using MsieJavaScriptEngine.Resources; + +using WrapperCompilationException = MsieJavaScriptEngine.JsCompilationException; +using WrapperEngineException = MsieJavaScriptEngine.JsEngineException; +using WrapperEngineLoadException = MsieJavaScriptEngine.JsEngineLoadException; +using WrapperException = MsieJavaScriptEngine.JsException; +using WrapperFatalException = MsieJavaScriptEngine.JsFatalException; +using WrapperInterruptedException = MsieJavaScriptEngine.JsInterruptedException; +using WrapperRuntimeException = MsieJavaScriptEngine.JsRuntimeException; +using WrapperScriptException = MsieJavaScriptEngine.JsScriptException; +using WrapperUsageException = MsieJavaScriptEngine.JsUsageException; + +using OriginalEngineException = MsieJavaScriptEngine.JsRt.JsEngineException; +using OriginalException = MsieJavaScriptEngine.JsRt.JsException; +using OriginalFatalException = MsieJavaScriptEngine.JsRt.JsFatalException; +using OriginalScriptException = MsieJavaScriptEngine.JsRt.Ie.IeJsScriptException; +using OriginalUsageException = MsieJavaScriptEngine.JsRt.JsUsageException; + +namespace MsieJavaScriptEngine.JsRt.Ie { - using System; - using System.Globalization; - using System.Linq; - - using Constants; - using Resources; - using Utilities; - /// - /// “IE” JsRT version of Chakra JavaScript engine + /// “IE” JsRT version of Chakra JS engine /// internal sealed class ChakraIeJsRtJsEngine : ChakraJsRtJsEngineBase { @@ -19,73 +37,87 @@ internal sealed class ChakraIeJsRtJsEngine : ChakraJsRtJsEngineBase const string LOWER_IE_VERSION = "11"; /// - /// Instance of JavaScript runtime + /// Instance of JS runtime /// private IeJsRuntime _jsRuntime; /// - /// Instance of JavaScript context - /// - private readonly IeJsContext _jsContext; - - /// - /// Flag indicating whether this JavaScript engine is supported + /// Instance of JS context /// - private static bool? _isSupported; + private IeJsContext _jsContext; /// - /// Support synchronizer + /// Type mapper /// - private static readonly object _supportSynchronizer = new object(); + private IeTypeMapper _typeMapper; /// - /// Run synchronizer + /// Flag indicating whether this JS engine is supported /// - private readonly object _runSynchronizer = new object(); + private static bool? _isSupported; /// - /// Flag that object is destroyed + /// Support synchronizer /// - private StatedFlag _disposedFlag = new StatedFlag(); + private static readonly Lock _supportSynchronizer = new Lock(); /// - /// Constructs instance of the Chakra “IE” JsRT JavaScript engine + /// Constructs an instance of the Chakra “IE” JsRT engine /// - /// Flag for whether to enable script debugging features - public ChakraIeJsRtJsEngine(bool enableDebugging) - : base(JsEngineMode.ChakraIeJsRt, enableDebugging) + /// JS engine settings + public ChakraIeJsRtJsEngine(JsEngineSettings settings) + : base(settings) { + _typeMapper = new IeTypeMapper(settings.AllowReflection); + try { - _jsRuntime = CreateJsRuntime(); - _jsContext = _jsRuntime.CreateContext(); + _dispatcher.Invoke(() => + { + _jsRuntime = CreateJsRuntime(); + _jsContext = _jsRuntime.CreateContext(); + if (_jsContext.IsValid) + { + _jsContext.AddRef(); + + if (_settings.EnableDebugging) + { + using (new IeJsScope(_jsContext)) + { + IeJsContext.StartDebugging(); + } + } + } + }); } - catch (JsUsageException e) + catch (DllNotFoundException e) { - string errorMessage; - if (e.ErrorCode == JsErrorCode.WrongThread) - { - errorMessage = Strings.Runtime_JsEnginesConflictOnMachine; - } - else - { - errorMessage = string.Format(Strings.Runtime_IeJsEngineNotLoaded, - _engineModeName, LOWER_IE_VERSION, e.Message); - } - - throw new JsEngineLoadException(errorMessage, _engineModeName); + throw WrapTypeLoadException(e); + } +#if NETSTANDARD1_3 + catch (TypeLoadException e) +#else + catch (EntryPointNotFoundException e) +#endif + { + throw WrapTypeLoadException(e); } catch (Exception e) { - throw new JsEngineLoadException( - string.Format(Strings.Runtime_IeJsEngineNotLoaded, - _engineModeName, LOWER_IE_VERSION, e.Message), _engineModeName); + throw JsErrorHelpers.WrapEngineLoadException(e, _engineModeName, true); + } + finally + { + if (!_jsContext.IsValid) + { + Dispose(); + } } } /// - /// Destructs instance of the Chakra “IE” JsRT JavaScript engine + /// Destructs an instance of the Chakra “IE” JsRT engine /// ~ChakraIeJsRtJsEngine() { @@ -94,21 +126,18 @@ public ChakraIeJsRtJsEngine(bool enableDebugging) /// - /// Creates a instance of JavaScript runtime with special settings + /// Creates a instance of JS runtime with special settings /// - /// Instance of JavaScript runtime with special settings + /// Instance of JS runtime with special settings private static IeJsRuntime CreateJsRuntime() { - var jsRuntime = IeJsRuntime.Create(JsRuntimeAttributes.AllowScriptInterrupt, - JsRuntimeVersion.VersionEdge, null); - - return jsRuntime; + return IeJsRuntime.Create(JsRuntimeAttributes.AllowScriptInterrupt, JsRuntimeVersion.VersionEdge, null); } /// - /// Checks a support of the Chakra “IE” JsRT JavaScript engine + /// Checks a support of the Chakra “IE” JsRT engine /// - /// Result of check (true - supports; false - does not support) + /// Result of check (true - supports; false - does not support) public static bool IsSupported() { if (_isSupported.HasValue) @@ -132,7 +161,7 @@ public static bool IsSupported() } catch (DllNotFoundException e) { - if (e.Message.IndexOf("'" + DllName.JScript9 + "'", StringComparison.OrdinalIgnoreCase) != -1) + if (e.Message.ContainsQuotedValue(DllName.JScript9)) { _isSupported = false; } @@ -141,11 +170,15 @@ public static bool IsSupported() _isSupported = null; } } +#if NETSTANDARD1_3 + catch (TypeLoadException e) +#else catch (EntryPointNotFoundException e) +#endif { string message = e.Message; - if (message.IndexOf("'" + DllName.JScript9 + "'", StringComparison.OrdinalIgnoreCase) != -1 - && message.IndexOf("'JsCreateRuntime'", StringComparison.OrdinalIgnoreCase) != -1) + if (message.ContainsQuotedValue(DllName.JScript9) + && message.ContainsQuotedValue("JsCreateRuntime")) { _isSupported = false; } @@ -164,309 +197,460 @@ public static bool IsSupported() } /// - /// Makes a mapping of value from the host type to a script type + /// Adds a reference to the value /// - /// The source value - /// The mapped value - private IeJsValue MapToScriptType(object value) + /// The value + private static void AddReferenceToValue(IeJsValue value) { - if (value == null) + if (CanHaveReferences(value)) { - return IeJsValue.Null; - } - - if (value is Undefined) - { - return IeJsValue.Undefined; - } - - var typeCode = Type.GetTypeCode(value.GetType()); - - switch (typeCode) - { - case TypeCode.Boolean: - return IeJsValue.FromBoolean((bool)value); - case TypeCode.Int32: - return IeJsValue.FromInt32((int)value); - case TypeCode.Double: - return IeJsValue.FromDouble((double)value); - case TypeCode.String: - return IeJsValue.FromString((string)value); - default: - object processedValue = !TypeConverter.IsPrimitiveType(typeCode) ? - new HostObject(value, _engineMode) : value; - return IeJsValue.FromObject(processedValue); + value.AddRef(); } } /// - /// Makes a mapping of array items from the host type to a script type + /// Removes a reference to the value /// - /// The source array - /// The mapped array - private IeJsValue[] MapToScriptType(object[] args) + /// The value + private static void RemoveReferenceToValue(IeJsValue value) { - return args.Select(MapToScriptType).ToArray(); + if (CanHaveReferences(value)) + { + value.Release(); + } } /// - /// 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(IeJsValue value) + /// The value + /// Result of check (true - may have; false - may not have) + private static bool CanHaveReferences(IeJsValue value) { JsValueType valueType = value.ValueType; - IeJsValue 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 = 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: - processedValue = value.ConvertToObject(); - object obj = processedValue.ToObject(); - - if (!TypeConverter.IsPrimitiveType(obj.GetType())) - { - var hostObj = obj as HostObject; - result = hostObj != null ? hostObj.Target : obj; - } - else - { - result = obj; - } - break; + return false; default: - throw new ArgumentOutOfRangeException(); + return true; } - - return result; } - /// - /// Makes a mapping of array itemp from the script type to a host type - /// - /// The source array - /// The mapped array - private object[] MapToHostType(IeJsValue[] args) - { - return args.Select(MapToHostType).ToArray(); - } + #region Mapping - private JsRuntimeException ConvertJsExceptionToJsRuntimeException( - JsException jsException) + private WrapperException WrapJsException(OriginalException originalException, + string defaultDocumentName = null) { - string message = jsException.Message; - string category = string.Empty; + 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; - var jsScriptException = jsException as IeJsScriptException; - if (jsScriptException != null) + var originalScriptException = originalException as OriginalScriptException; + if (originalScriptException is not null) { - category = "Script error"; - IeJsValue errorValue = jsScriptException.Error; + IeJsValue errorValue = originalScriptException.Error; - IeJsPropertyId messagePropertyId = IeJsPropertyId.FromString("message"); - IeJsValue messagePropertyValue = errorValue.GetProperty(messagePropertyId); - string scriptMessage = messagePropertyValue.ConvertToString().ToString(); - if (!string.IsNullOrWhiteSpace(scriptMessage)) + if (errorValue.IsValid) { - message = string.Format("{0}: {1}", message.TrimEnd('.'), scriptMessage); + JsValueType errorValueType = errorValue.ValueType; + + if (errorValueType == JsValueType.Error + || errorValue.ValueType == JsValueType.Object) + { + IeJsValue messagePropertyValue = errorValue.GetProperty("message"); + string localDescription = messagePropertyValue.ConvertToString().ToString(); + if (!string.IsNullOrWhiteSpace(localDescription)) + { + description = localDescription; + } + + IeJsValue namePropertyValue = errorValue.GetProperty("name"); + type = namePropertyValue.ValueType == JsValueType.String ? + namePropertyValue.ToString() : string.Empty; + + IeJsPropertyId descriptionPropertyId = IeJsPropertyId.FromString("description"); + if (errorValue.HasProperty(descriptionPropertyId)) + { + IeJsValue descriptionPropertyValue = errorValue.GetProperty(descriptionPropertyId); + localDescription = descriptionPropertyValue.ConvertToString().ToString(); + if (!string.IsNullOrWhiteSpace(localDescription)) + { + description = localDescription; + } + } + + if (type == JsErrorType.Syntax) + { + errorCode = JsErrorCode.ScriptCompile; + } + else + { + IeJsPropertyId numberPropertyId = IeJsPropertyId.FromString("number"); + if (errorValue.HasProperty(numberPropertyId)) + { + IeJsValue numberPropertyValue = errorValue.GetProperty(numberPropertyId); + int errorNumber = numberPropertyValue.ValueType == JsValueType.Number ? + numberPropertyValue.ToInt32() : 0; + errorCode = (JsErrorCode)errorNumber; + } + } + + IeJsPropertyId stackPropertyId = IeJsPropertyId.FromString("stack"); + if (errorValue.HasProperty(stackPropertyId)) + { + IeJsValue stackPropertyValue = errorValue.GetProperty(stackPropertyId); + string messageWithTypeAndCallStack = stackPropertyValue.ValueType == JsValueType.String ? + stackPropertyValue.ToString() : string.Empty; + string messageWithType = errorValue.ConvertToString().ToString(); + string rawCallStack = JsErrorHelpers.GetCallStackFromMessage( + messageWithTypeAndCallStack, messageWithType); + CallStackItem[] callStackItems = JsErrorHelpers.ParseCallStack(rawCallStack); + + if (callStackItems.Length > 0) + { + FixCallStackItems(callStackItems); + + CallStackItem firstCallStackItem = callStackItems[0]; + if (firstCallStackItem.DocumentName.Length > 0) + { + documentName = firstCallStackItem.DocumentName; + } + lineNumber = firstCallStackItem.LineNumber; + columnNumber = firstCallStackItem.ColumnNumber; + callStack = JsErrorHelpers.StringifyCallStackItems(callStackItems); + } + + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, callStack); + } + else + { + type = errorCode == JsErrorCode.ScriptCompile ? JsErrorType.Syntax : type; + + IeJsPropertyId linePropertyId = IeJsPropertyId.FromString("line"); + if (errorValue.HasProperty(linePropertyId)) + { + IeJsValue linePropertyValue = errorValue.GetProperty(linePropertyId); + lineNumber = linePropertyValue.ValueType == JsValueType.Number ? + linePropertyValue.ToInt32() + 1 : 0; + } + + IeJsPropertyId columnPropertyId = IeJsPropertyId.FromString("column"); + if (errorValue.HasProperty(columnPropertyId)) + { + IeJsValue columnPropertyValue = errorValue.GetProperty(columnPropertyId); + columnNumber = columnPropertyValue.ValueType == JsValueType.Number ? + columnPropertyValue.ToInt32() + 1 : 0; + } + + string sourceLine = string.Empty; + IeJsPropertyId sourcePropertyId = IeJsPropertyId.FromString("source"); + if (errorValue.HasProperty(sourcePropertyId)) + { + IeJsValue sourcePropertyValue = errorValue.GetProperty(sourcePropertyId); + sourceLine = sourcePropertyValue.ValueType == JsValueType.String ? + sourcePropertyValue.ToString() : string.Empty; + if (sourceLine != "undefined") + { + sourceFragment = TextHelpers.GetTextFragmentFromLine(sourceLine, columnNumber); + } + } + + message = JsErrorHelpers.GenerateScriptErrorMessage(type, description, documentName, + lineNumber, columnNumber, sourceFragment); + } + } + else if (errorValueType == JsValueType.String) + { + message = errorValue.ToString(); + description = message; + } + else + { + message = errorValue.ConvertToString().ToString(); + description = message; + } } - IeJsPropertyId linePropertyId = IeJsPropertyId.FromString("line"); - if (errorValue.HasProperty(linePropertyId)) + WrapperScriptException wrapperScriptException; + if (errorCode == JsErrorCode.ScriptCompile) { - IeJsValue linePropertyValue = errorValue.GetProperty(linePropertyId); - lineNumber = (int)linePropertyValue.ConvertToNumber().ToDouble() + 1; + wrapperScriptException = new WrapperCompilationException(message, _engineModeName, + originalScriptException); } + else if (errorCode == JsErrorCode.ScriptTerminated) + { + wrapperScriptException = new WrapperInterruptedException(CommonStrings.Runtime_ScriptInterrupted, + _engineModeName, originalScriptException); - IeJsPropertyId columnPropertyId = IeJsPropertyId.FromString("column"); - if (errorValue.HasProperty(columnPropertyId)) + // Restore a JS engine after interruption + _jsRuntime.Disabled = false; + } + else { - IeJsValue columnPropertyValue = errorValue.GetProperty(columnPropertyId); - columnNumber = (int)columnPropertyValue.ConvertToNumber().ToDouble() + 1; + wrapperScriptException = new WrapperRuntimeException(message, _engineModeName, + originalScriptException) + { + CallStack = callStack + }; } + wrapperScriptException.Type = type; + wrapperScriptException.DocumentName = documentName; + wrapperScriptException.LineNumber = lineNumber; + wrapperScriptException.ColumnNumber = columnNumber; + wrapperScriptException.SourceFragment = sourceFragment; - IeJsPropertyId sourcePropertyId = IeJsPropertyId.FromString("source"); - if (errorValue.HasProperty(sourcePropertyId)) + wrapperException = wrapperScriptException; + } + else + { + if (originalException is OriginalUsageException) + { + wrapperException = new WrapperUsageException(message, _engineModeName, originalException); + } + else if (originalException is OriginalEngineException) { - IeJsValue sourcePropertyValue = errorValue.GetProperty(sourcePropertyId); - sourceFragment = sourcePropertyValue.ConvertToString().ToString(); + wrapperException = new WrapperEngineException(message, _engineModeName, originalException); + } + else if (originalException is OriginalFatalException) + { + wrapperException = new WrapperFatalException(message, _engineModeName, originalException); + } + else + { + wrapperException = new WrapperException(message, _engineModeName, originalException); } } - else if (jsException is JsUsageException) + + wrapperException.Description = description; + + return wrapperException; + } + + /// + /// Fixes a function name in call stack items + /// + /// An array of instances + private static void FixCallStackItems(CallStackItem[] callStackItems) + { + foreach (CallStackItem callStackItem in callStackItems) { - category = "Usage error"; + if (callStackItem.FunctionName == "Unknown script code") + { + callStackItem.FunctionName = "Global code"; + } } - else if (jsException is JsEngineException) + } + + private WrapperEngineLoadException WrapTypeLoadException(TypeLoadException originalTypeLoadException) + { + string originalMessage = originalTypeLoadException.Message; + bool isDllNotFound = originalTypeLoadException is DllNotFoundException; + string description; + string message; + + if (originalMessage.ContainsQuotedValue(DllName.JScript9) + && (isDllNotFound || originalMessage.ContainsQuotedValue("JsCreateRuntime"))) { - category = "Engine error"; + var stringBuilderPool = StringBuilderPool.Shared; + StringBuilder descriptionBuilder = stringBuilderPool.Rent(); + if (isDllNotFound) + { + descriptionBuilder.AppendFormat(CommonStrings.Engine_AssemblyNotRegistered, DllName.JScript9); + descriptionBuilder.Append(" "); + } + descriptionBuilder.AppendFormat(CommonStrings.Engine_IeInstallationRequired, LOWER_IE_VERSION); + + description = descriptionBuilder.ToString(); + stringBuilderPool.Return(descriptionBuilder); + + message = JsErrorHelpers.GenerateEngineLoadErrorMessage(description, _engineModeName); } - else if (jsException is JsFatalException) + else { - category = "Fatal error"; + description = originalMessage; + message = JsErrorHelpers.GenerateEngineLoadErrorMessage(description, _engineModeName, true); } - var jsEngineException = new JsRuntimeException(message, _engineModeName) + var wrapperEngineLoadException = new WrapperEngineLoadException(message, _engineModeName, + originalTypeLoadException) { - ErrorCode = ((uint)jsException.ErrorCode).ToString(CultureInfo.InvariantCulture), - Category = category, - LineNumber = lineNumber, - ColumnNumber = columnNumber, - SourceFragment = sourceFragment, - HelpLink = jsException.HelpLink + Description = description }; - return jsEngineException; + return wrapperEngineLoadException; } - protected override void InnerStartDebugging() - { - if (Environment.Is64BitProcess) - { - var processDebugManager64 = (IProcessDebugManager64)new ProcessDebugManager(); - IDebugApplication64 debugApplication64; - processDebugManager64.GetDefaultApplication(out debugApplication64); + #endregion - IeJsContext.StartDebugging(debugApplication64); - } - else - { - var processDebugManager32 = (IProcessDebugManager32)new ProcessDebugManager(); - IDebugApplication32 debugApplication32; - processDebugManager32.GetDefaultApplication(out debugApplication32); + #region ChakraJsRtJsEngineBase overrides - IeJsContext.StartDebugging(debugApplication32); - } + #region IInnerJsEngine implementation + + public override bool SupportsScriptPrecompilation + { + get { return true; } } - private void InvokeScript(Action action) + + public override PrecompiledScript Precompile(string code, string documentName) { - lock (_runSynchronizer) - using (new IeJsScope(_jsContext)) + PrecompiledScript precompiledScript = _dispatcher.Invoke(() => { - if (_enableDebugging) + using (new IeJsScope(_jsContext)) { - StartDebugging(); - } + try + { + byte[] cachedBytes = IeJsContext.SerializeScript(code); - try - { - action(); - } - catch (JsException e) - { - throw ConvertJsExceptionToJsRuntimeException(e); + return new PrecompiledScript(_engineModeName, code, cachedBytes, documentName); + } + catch (OriginalException e) + { + throw WrapJsException(e, documentName); + } } - } + }); + + return precompiledScript; } - private T InvokeScript(Func func) + public override object Evaluate(string expression, string documentName) { - lock (_runSynchronizer) - using (new IeJsScope(_jsContext)) + object result = _dispatcher.Invoke(() => { - if (_enableDebugging) + using (new IeJsScope(_jsContext)) { - StartDebugging(); - } + try + { + IeJsValue resultValue = IeJsContext.RunScript(expression, _jsSourceContext++, + documentName); - try - { - return func(); - } - catch (JsException e) - { - throw ConvertJsExceptionToJsRuntimeException(e); + return _typeMapper.MapToHostType(resultValue); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } } - } + }); + + return result; } - /// - /// Destroys object - /// - /// Flag, allowing destruction of - /// managed objects contained in fields of class - private void Dispose(bool disposing) + public override void Execute(string code, string documentName) { - lock (_runSynchronizer) + _dispatcher.Invoke(() => { - if (_disposedFlag.Set()) + using (new IeJsScope(_jsContext)) { - _jsRuntime.Dispose(); + try + { + IeJsContext.RunScript(code, _jsSourceContext++, documentName); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } } - } - } - - #region IInnerJsEngine implementation - - public override string Mode - { - get { return _engineModeName; } + }); } - public override object Evaluate(string expression) + public override void Execute(PrecompiledScript precompiledScript) { - object result = InvokeScript(() => + _dispatcher.Invoke(() => { - IeJsValue resultValue = IeJsContext.RunScript(expression); - - return MapToHostType(resultValue); + using (new IeJsScope(_jsContext)) + { + try + { + IeJsContext.RunSerializedScript(precompiledScript.Code, precompiledScript.CachedBytes, + _jsSourceContext++, precompiledScript.DocumentName); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + finally + { + GC.KeepAlive(precompiledScript); + } + } }); - - return result; - } - - public override void Execute(string code) - { - InvokeScript(() => IeJsContext.RunScript(code)); } public override object CallFunction(string functionName, params object[] args) { - object result = InvokeScript(() => + object result = _dispatcher.Invoke(() => { - IeJsValue globalObj = IeJsValue.GlobalObject; - IeJsPropertyId functionId = IeJsPropertyId.FromString(functionName); - - bool functionExist = globalObj.HasProperty(functionId); - if (!functionExist) + using (new IeJsScope(_jsContext)) { - throw new JsRuntimeException( - string.Format(Strings.Runtime_FunctionNotExist, functionName)); + try + { + IeJsValue globalObj = IeJsValue.GlobalObject; + IeJsPropertyId functionId = IeJsPropertyId.FromString(functionName); + + bool functionExist = globalObj.HasProperty(functionId); + if (!functionExist) + { + throw new WrapperRuntimeException( + string.Format(CommonStrings.Runtime_FunctionNotExist, functionName), + _engineModeName + ); + } + + IeJsValue resultValue; + IeJsValue functionValue = globalObj.GetProperty(functionId); + + int argCount = args.Length; + if (argCount > 0) + { + int processedArgCount = argCount + 1; + var processedArgs = new IeJsValue[processedArgCount]; + processedArgs[0] = globalObj; + + for (int argIndex = 0; argIndex < argCount; argIndex++) + { + IeJsValue processedArg = _typeMapper.MapToScriptType(args[argIndex]); + AddReferenceToValue(processedArg); + + processedArgs[argIndex + 1] = processedArg; + } + + try + { + resultValue = functionValue.CallFunction(processedArgs); + } + finally + { + for (int argIndex = 1; argIndex < processedArgCount; argIndex++) + { + RemoveReferenceToValue(processedArgs[argIndex]); + } + } + } + else + { + resultValue = functionValue.CallFunction(globalObj); + } + + return _typeMapper.MapToHostType(resultValue); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } } - - var processedArgs = MapToScriptType(args); - var allProcessedArgs = new[] { globalObj }.Concat(processedArgs).ToArray(); - - IeJsValue functionValue = globalObj.GetProperty(functionId); - IeJsValue resultValue = functionValue.CallFunction(allProcessedArgs); - - return MapToHostType(resultValue); }); return result; @@ -474,19 +658,29 @@ public override object CallFunction(string functionName, params object[] args) public override bool HasVariable(string variableName) { - bool result = InvokeScript(() => + bool result = _dispatcher.Invoke(() => { - IeJsValue globalObj = IeJsValue.GlobalObject; - IeJsPropertyId variableId = IeJsPropertyId.FromString(variableName); - bool variableExist = globalObj.HasProperty(variableId); - - if (variableExist) + using (new IeJsScope(_jsContext)) { - IeJsValue variableValue = globalObj.GetProperty(variableId); - variableExist = (variableValue.ValueType != JsValueType.Undefined); - } + try + { + IeJsValue globalObj = IeJsValue.GlobalObject; + IeJsPropertyId variableId = IeJsPropertyId.FromString(variableName); + bool variableExist = globalObj.HasProperty(variableId); - return variableExist; + if (variableExist) + { + IeJsValue variableValue = globalObj.GetProperty(variableId); + variableExist = variableValue.ValueType != JsValueType.Undefined; + } + + return variableExist; + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } }); return result; @@ -494,12 +688,21 @@ public override bool HasVariable(string variableName) public override object GetVariableValue(string variableName) { - object result = InvokeScript(() => + object result = _dispatcher.Invoke(() => { - IeJsPropertyId variableId = IeJsPropertyId.FromString(variableName); - IeJsValue variableValue = IeJsValue.GlobalObject.GetProperty(variableId); + using (new IeJsScope(_jsContext)) + { + try + { + IeJsValue variableValue = IeJsValue.GlobalObject.GetProperty(variableName); - return MapToHostType(variableValue); + return _typeMapper.MapToHostType(variableValue); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } }); return result; @@ -507,51 +710,104 @@ public override object GetVariableValue(string variableName) public override void SetVariableValue(string variableName, object value) { - InvokeScript(() => + _dispatcher.Invoke(() => { - IeJsPropertyId variableId = IeJsPropertyId.FromString(variableName); - IeJsValue inputValue = MapToScriptType(value); - - IeJsValue.GlobalObject.SetProperty(variableId, inputValue, true); + using (new IeJsScope(_jsContext)) + { + try + { + IeJsValue inputValue = _typeMapper.MapToScriptType(value); + AddReferenceToValue(inputValue); + + try + { + IeJsValue.GlobalObject.SetProperty(variableName, inputValue, true); + } + finally + { + RemoveReferenceToValue(inputValue); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } }); } public override void RemoveVariable(string variableName) { - InvokeScript(() => + _dispatcher.Invoke(() => { - IeJsValue globalObj = IeJsValue.GlobalObject; - IeJsPropertyId variableId = IeJsPropertyId.FromString(variableName); - - if (globalObj.HasProperty(variableId)) + using (new IeJsScope(_jsContext)) { - globalObj.SetProperty(variableId, IeJsValue.Undefined, true); + try + { + IeJsValue globalObj = IeJsValue.GlobalObject; + IeJsPropertyId variableId = IeJsPropertyId.FromString(variableName); + + if (globalObj.HasProperty(variableId)) + { + globalObj.SetProperty(variableId, IeJsValue.Undefined, true); + } + } + catch (OriginalException e) + { + throw WrapJsException(e); + } } }); } public override void EmbedHostObject(string itemName, object value) { - InvokeScript(() => + _dispatcher.Invoke(() => { - IeJsValue processedValue = MapToScriptType(value); - IeJsPropertyId itemId = IeJsPropertyId.FromString(itemName); - - IeJsValue.GlobalObject.SetProperty(itemId, processedValue, true); + using (new IeJsScope(_jsContext)) + { + try + { + IeJsValue processedValue = _typeMapper.GetOrCreateScriptObject(value); + IeJsValue.GlobalObject.SetProperty(itemName, processedValue, true); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } }); } public override void EmbedHostType(string itemName, Type type) { - InvokeScript(() => + _dispatcher.Invoke(() => { - IeJsValue typeValue = IeJsValue.FromObject(new HostType(type, _engineMode)); - IeJsPropertyId itemId = IeJsPropertyId.FromString(itemName); - - IeJsValue.GlobalObject.SetProperty(itemId, typeValue, true); + using (new IeJsScope(_jsContext)) + { + try + { + IeJsValue typeValue = _typeMapper.GetOrCreateScriptType(type); + IeJsValue.GlobalObject.SetProperty(itemName, typeValue, true); + } + catch (OriginalException e) + { + throw WrapJsException(e); + } + } }); } + public override void Interrupt() + { + _jsRuntime.Disabled = true; + } + + public override void CollectGarbage() + { + _jsRuntime.CollectGarbage(); + } + #endregion #region IDisposable implementation @@ -565,6 +821,48 @@ 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 is not null) + { + _dispatcher.Invoke(DisposeUnmanagedResources); + + _dispatcher.Dispose(); + _dispatcher = null; + } + + if (_typeMapper is not null) + { + _typeMapper.Dispose(); + _typeMapper = 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/MsieJavaScriptEngine/JsRt/Ie/IDebugApplication32.cs b/src/MsieJavaScriptEngine/JsRt/Ie/IDebugApplication32.cs deleted file mode 100644 index fd3ae56..0000000 --- a/src/MsieJavaScriptEngine/JsRt/Ie/IDebugApplication32.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MsieJavaScriptEngine.JsRt.Ie -{ - using System.Runtime.InteropServices; - - /// - /// IDebugApplication32 COM interface - /// - [Guid("51973C32-CB0C-11d0-B5C9-00A0244A0E7A")] - internal interface IDebugApplication32 - { } -} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/IDebugApplication64.cs b/src/MsieJavaScriptEngine/JsRt/Ie/IDebugApplication64.cs deleted file mode 100644 index 29bd944..0000000 --- a/src/MsieJavaScriptEngine/JsRt/Ie/IDebugApplication64.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MsieJavaScriptEngine.JsRt.Ie -{ - using System.Runtime.InteropServices; - - /// - /// IDebugApplication64 COM interface - /// - [Guid("4dedc754-04c7-4f10-9e60-16a390fe6e62")] - internal interface IDebugApplication64 - { } -} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/IDebugDocumentHelper32.cs b/src/MsieJavaScriptEngine/JsRt/Ie/IDebugDocumentHelper32.cs deleted file mode 100644 index fb8d0bc..0000000 --- a/src/MsieJavaScriptEngine/JsRt/Ie/IDebugDocumentHelper32.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MsieJavaScriptEngine.JsRt.Ie -{ - using System.Runtime.InteropServices; - - /// - /// IDebugDocumentHelper32 COM interface - /// - [Guid("51973C26-CB0C-11d0-B5C9-00A0244A0E7A")] - internal interface IDebugDocumentHelper32 - { } -} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/IDebugDocumentHelper64.cs b/src/MsieJavaScriptEngine/JsRt/Ie/IDebugDocumentHelper64.cs deleted file mode 100644 index 6d024f0..0000000 --- a/src/MsieJavaScriptEngine/JsRt/Ie/IDebugDocumentHelper64.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace MsieJavaScriptEngine.JsRt.Ie -{ - using System.Runtime.InteropServices; - - /// - /// IDebugDocumentHelper64 COM interface - /// - [Guid("c4c7363c-20fd-47f9-bd82-4855e0150871")] - internal interface IDebugDocumentHelper64 - { } -} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/IeJsContext.cs b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsContext.cs index 5e0b626..81dcb63 100644 --- a/src/MsieJavaScriptEngine/JsRt/Ie/IeJsContext.cs +++ b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsContext.cs @@ -1,7 +1,10 @@ -namespace MsieJavaScriptEngine.JsRt.Ie -{ - using System; +using System; + +using MsieJavaScriptEngine.ActiveScript.Debugging; +using MsieJavaScriptEngine.Utilities; +namespace MsieJavaScriptEngine.JsRt.Ie +{ /// /// “IE” script context /// @@ -79,23 +82,6 @@ public static bool HasException } } - /// - /// Gets a value indicating whether the heap of the current context is being enumerated - /// - /// - /// Requires an active script context. - /// - public static bool IsEnumeratingHeap - { - get - { - bool isEnumerating; - IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsIsEnumeratingHeap(out isEnumerating)); - - return isEnumerating; - } - } - /// /// Gets a runtime that the context belongs to /// @@ -158,71 +144,53 @@ 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 + /// A 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 + /// A function representing the script code public static IeJsValue ParseScript(string script, JsSourceContext sourceContext, string sourceName) { IeJsValue result; - IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsParseScript(script, sourceContext, sourceName, out result)); + IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsParseScript(script, sourceContext, sourceName, + out result)); 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 - /// The cookie identifying the script that can be used + /// A 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 IeJsValue ParseScript(string script, byte[] buffer, JsSourceContext sourceContext, string sourceName) + /// A function representing the script code + public static IeJsValue ParseSerializedScript(string script, byte[] buffer, JsSourceContext sourceContext, + string sourceName) { IeJsValue result; - IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsParseSerializedScript(script, buffer, sourceContext, sourceName, out result)); + IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsParseSerializedScript(script, buffer, sourceContext, + sourceName, out result)); return result; } - /// - /// 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 IeJsValue ParseScript(string script) - { - return ParseScript(script, JsSourceContext.None, string.Empty); - } - - /// - /// 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 IeJsValue ParseScript(string script, byte[] buffer) - { - return ParseScript(script, buffer, JsSourceContext.None, string.Empty); - } - /// /// Executes a script /// @@ -230,13 +198,15 @@ public static IeJsValue 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 + /// A 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 IeJsValue RunScript(string script, JsSourceContext sourceContext, string sourceName) { IeJsValue result; - IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsRunScript(script, sourceContext, sourceName, out result)); + IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsRunScript(script, sourceContext, sourceName, + out result)); return result; } @@ -245,54 +215,36 @@ public static IeJsValue RunScript(string script, JsSourceContext sourceContext, /// Runs a serialized 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 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 + /// A 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 IeJsValue RunScript(string script, byte[] buffer, JsSourceContext sourceContext, string sourceName) + public static IeJsValue RunSerializedScript(string script, byte[] buffer, JsSourceContext sourceContext, + string sourceName) { IeJsValue result; - IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsRunSerializedScript(script, buffer, sourceContext, sourceName, out result)); + IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsRunSerializedScript(script, buffer, sourceContext, + sourceName, out result)); return result; } - /// - /// Executes a script - /// - /// - /// Requires an active script context. - /// - /// The script to run - /// The result of the script, if any - public static IeJsValue RunScript(string script) - { - return RunScript(script, JsSourceContext.None, string.Empty); - } - - /// - /// Runs a serialized script - /// - /// - /// Requires an active script context. - /// - /// The source code of the serialized script - /// The serialized script - /// The result of the script, if any - public static IeJsValue RunScript(string script, byte[] buffer) - { - return RunScript(script, buffer, JsSourceContext.None, string.Empty); - } - /// /// Serializes a parsed script to a buffer than can be reused /// /// /// - /// 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. /// @@ -301,14 +253,21 @@ public static IeJsValue 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) + /// The buffer to put the serialized script into + public static byte[] SerializeScript(string script) { - var bufferSize = (ulong)buffer.Length; - IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsSerializeScript(script, buffer, ref bufferSize)); + byte[] buffer = null; + uint bufferSize = 0; + + JsErrorCode errorCode = IeNativeMethods.JsSerializeScript(script, buffer, ref bufferSize); + IeJsErrorHelpers.ThrowIfError(errorCode); + + buffer = new byte[(int)bufferSize]; + + errorCode = IeNativeMethods.JsSerializeScript(script, buffer, ref bufferSize); + IeJsErrorHelpers.ThrowIfError(errorCode); - return bufferSize; + return buffer; } /// @@ -360,7 +319,7 @@ public static void SetException(IeJsValue exception) /// The debug application to use for debugging public static void StartDebugging(IDebugApplication64 debugApplication) { - if (!Environment.Is64BitProcess) + if (!Utils.Is64BitProcess()) { throw new InvalidOperationException(); } @@ -374,7 +333,7 @@ public static void StartDebugging(IDebugApplication64 debugApplication) /// The debug application to use for debugging public static void StartDebugging(IDebugApplication32 debugApplication) { - if (Environment.Is64BitProcess) + if (Utils.Is64BitProcess()) { throw new InvalidOperationException(); } @@ -383,62 +342,33 @@ public static void StartDebugging(IDebugApplication32 debugApplication) } /// - /// Starts profiling in the current context - /// - /// - /// Requires an active script context. - /// - /// The profiling callback to use - /// The profiling events to callback with - /// The context to pass to the profiling callback - public static void StartProfiling(IActiveScriptProfilerCallback callback, ProfilerEventMask eventMask, int context) - { - IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsStartProfiling(callback, eventMask, context)); - } - - /// - /// Stops profiling in the current context + /// Starts debugging in the context /// - /// - /// - /// Will not return an error if profiling has not started. - /// - /// - /// Requires an active script context. - /// - /// - /// The reason for stopping profiling to pass to the profiler callback - public static void StopProfiling(int reason) + public static void StartDebugging() { - IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsStopProfiling(reason)); - } + if (Utils.Is64BitProcess()) + { + var processDebugManager64 = (IProcessDebugManager64)new ProcessDebugManager(); + IDebugApplication64 debugApplication64; + processDebugManager64.GetDefaultApplication(out debugApplication64); - /// - /// Enumerates a heap of the current context - /// - /// - /// - /// While the heap is being enumerated, the current context cannot be removed, and all calls to - /// modify the state of the context will fail until the heap enumerator is released. - /// - /// - /// Requires an active script context. - /// - /// - /// The heap enumerator - public static IActiveScriptProfilerHeapEnum EnumerateHeap() - { - IActiveScriptProfilerHeapEnum enumerator; - IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsEnumerateHeap(out enumerator)); + IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsStartDebugging(debugApplication64)); + } + else + { + var processDebugManager32 = (IProcessDebugManager32)new ProcessDebugManager(); + IDebugApplication32 debugApplication32; + processDebugManager32.GetDefaultApplication(out debugApplication32); - return enumerator; + IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsStartDebugging(debugApplication32)); + } } /// /// 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() @@ -453,7 +383,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/MsieJavaScriptEngine/JsRt/Ie/IeJsErrorHelpers.cs b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsErrorHelpers.cs index 0709541..b0346cd 100644 --- a/src/MsieJavaScriptEngine/JsRt/Ie/IeJsErrorHelpers.cs +++ b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsErrorHelpers.cs @@ -8,109 +8,213 @@ internal static class IeJsErrorHelpers /// /// 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.OutOfMemory: - throw new JsEngineException(error, "Out of memory."); + #endregion - case JsErrorCode.ScriptException: - { - IeJsValue errorObject; - JsErrorCode innerError = IeNativeMethods.JsGetAndClearException(out errorObject); + #region Engine - if (innerError != JsErrorCode.NoError) - { - throw new JsFatalException(innerError); - } + case JsErrorCode.OutOfMemory: + throw new JsEngineException(errorCode, "Out of memory."); - throw new IeJsScriptException(error, errorObject, "Script threw an exception."); - } + #endregion + + #region Script + case JsErrorCode.ScriptException: case JsErrorCode.ScriptCompile: { IeJsValue errorObject; - JsErrorCode innerError = IeNativeMethods.JsGetAndClearException(out errorObject); + JsErrorCode innerErrorCode = IeNativeMethods.JsGetAndClearException(out errorObject); - if (innerError != JsErrorCode.NoError) + if (innerErrorCode != JsErrorCode.NoError) { - throw new JsFatalException(innerError); + throw new JsFatalException(innerErrorCode); } - throw new IeJsScriptException(error, errorObject, "Compile error."); + string message = errorCode == JsErrorCode.ScriptCompile ? + "Compile error." : "Script threw an exception."; + + throw new IeJsScriptException(errorCode, errorObject, message); } case JsErrorCode.ScriptTerminated: - throw new IeJsScriptException(error, IeJsValue.Invalid, "Script was terminated."); + throw new IeJsScriptException(errorCode, IeJsValue.Invalid, "Script was terminated."); case JsErrorCode.ScriptEvalDisabled: - throw new IeJsScriptException(error, IeJsValue.Invalid, "Eval of strings is disabled in this runtime."); + throw new IeJsScriptException(errorCode, IeJsValue.Invalid, "Eval of strings is disabled in this runtime."); + + #endregion + + #region Fatal case JsErrorCode.Fatal: - throw new JsFatalException(error); + throw new JsFatalException(errorCode); + + #endregion default: - throw new JsFatalException(error); + throw new JsFatalException(errorCode); } } } + + + /// + /// Creates a new JavaScript Error object + /// + /// + /// Requires an active script context. + /// + /// The message that describes the error + /// The new error object + public static IeJsValue CreateError(string message) + { + IeJsValue messageValue = IeJsValue.FromString(message); + IeJsValue errorValue = IeJsValue.CreateError(messageValue); + + return errorValue; + } + + /// + /// Creates a new JavaScript RangeError error object + /// + /// + /// Requires an active script context. + /// + /// The message that describes the error + /// The new error object + public static IeJsValue CreateRangeError(string message) + { + IeJsValue messageValue = IeJsValue.FromString(message); + IeJsValue errorValue = IeJsValue.CreateRangeError(messageValue); + + return errorValue; + } + + /// + /// Creates a new JavaScript ReferenceError error object + /// + /// + /// Requires an active script context. + /// + /// The message that describes the error + /// The new error object + public static IeJsValue CreateReferenceError(string message) + { + IeJsValue messageValue = IeJsValue.FromString(message); + IeJsValue errorValue = IeJsValue.CreateReferenceError(messageValue); + + return errorValue; + } + + /// + /// Creates a new JavaScript SyntaxError error object + /// + /// + /// Requires an active script context. + /// + /// The message that describes the error + /// The new error object + public static IeJsValue CreateSyntaxError(string message) + { + IeJsValue messageValue = IeJsValue.FromString(message); + IeJsValue errorValue = IeJsValue.CreateSyntaxError(messageValue); + + return errorValue; + } + + /// + /// Creates a new JavaScript TypeError error object + /// + /// + /// Requires an active script context. + /// + /// The message that describes the error + /// The new error object + public static IeJsValue CreateTypeError(string message) + { + IeJsValue messageValue = IeJsValue.FromString(message); + IeJsValue errorValue = IeJsValue.CreateTypeError(messageValue); + + return errorValue; + } + + /// + /// Creates a new JavaScript URIError error object + /// + /// + /// Requires an active script context. + /// + /// The message that describes the error + /// The new error object + public static IeJsValue CreateUriError(string message) + { + IeJsValue messageValue = IeJsValue.FromString(message); + IeJsValue errorValue = IeJsValue.CreateUriError(messageValue); + + return errorValue; + } } } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/IeJsNativeFunction.cs b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsNativeFunction.cs index 55b9429..e7bb0a4 100644 --- a/src/MsieJavaScriptEngine/JsRt/Ie/IeJsNativeFunction.cs +++ b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsNativeFunction.cs @@ -1,13 +1,13 @@ -namespace MsieJavaScriptEngine.JsRt.Ie -{ - using System; - using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; +namespace MsieJavaScriptEngine.JsRt.Ie +{ /// /// “IE” 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/MsieJavaScriptEngine/JsRt/Ie/IeJsPropertyId.cs b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsPropertyId.cs index dd5b10a..906c656 100644 --- a/src/MsieJavaScriptEngine/JsRt/Ie/IeJsPropertyId.cs +++ b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsPropertyId.cs @@ -1,8 +1,8 @@ -namespace MsieJavaScriptEngine.JsRt.Ie -{ - using System; - using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; +namespace MsieJavaScriptEngine.JsRt.Ie +{ /// /// “IE” property identifier /// @@ -29,9 +29,7 @@ public static IeJsPropertyId Invalid /// Gets a name associated with the property ID /// /// - /// /// Requires an active script context. - /// /// public string Name { @@ -40,7 +38,7 @@ public string Name IntPtr buffer; IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsGetPropertyNameFromId(this, out buffer)); - return Marshal.PtrToStringAuto(buffer); + return Marshal.PtrToStringUni(buffer); } } diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/IeJsRuntime.cs b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsRuntime.cs index 2e4f30f..33d518b 100644 --- a/src/MsieJavaScriptEngine/JsRt/Ie/IeJsRuntime.cs +++ b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsRuntime.cs @@ -1,7 +1,10 @@ -namespace MsieJavaScriptEngine.JsRt.Ie -{ - using System; +using System; + +using MsieJavaScriptEngine.ActiveScript.Debugging; +using MsieJavaScriptEngine.Utilities; +namespace MsieJavaScriptEngine.JsRt.Ie +{ /// /// “IE” Chakra runtime /// @@ -15,9 +18,9 @@ /// time. /// /// - /// NOTE: A , unlike other objects in the Chakra hosting API, is not + /// NOTE: A , 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 IeJsRuntime : IDisposable @@ -101,16 +104,10 @@ 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 IeJsRuntime Create(JsRuntimeAttributes attributes, JsRuntimeVersion version, JsThreadServiceCallback threadServiceCallback) + public static IeJsRuntime Create() { - IeJsRuntime handle; - IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsCreateRuntime(attributes, version, threadServiceCallback, out handle)); - - return handle; + return Create(JsRuntimeAttributes.None, JsRuntimeVersion.Version11, null); } /// @@ -127,10 +124,16 @@ public static IeJsRuntime Create(JsRuntimeAttributes attributes, JsRuntimeVersio /// /// 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 IeJsRuntime Create() + public static IeJsRuntime Create(JsRuntimeAttributes attributes, JsRuntimeVersion version, JsThreadServiceCallback threadServiceCallback) { - return Create(JsRuntimeAttributes.None, JsRuntimeVersion.Version11, null); + IeJsRuntime handle; + IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsCreateRuntime(attributes, version, threadServiceCallback, out handle)); + + return handle; } /// @@ -149,7 +152,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. /// /// @@ -204,7 +207,7 @@ public void SetBeforeCollectCallback(IntPtr callbackState, JsBeforeCollectCallba public IeJsContext CreateContext(IDebugApplication64 debugApplication) { IeJsContext reference; - if (!Environment.Is64BitProcess) + if (!Utils.Is64BitProcess()) { throw new InvalidOperationException(); } @@ -225,7 +228,7 @@ public IeJsContext CreateContext(IDebugApplication64 debugApplication) public IeJsContext CreateContext(IDebugApplication32 debugApplication) { IeJsContext reference; - if (Environment.Is64BitProcess) + if (Utils.Is64BitProcess()) { throw new InvalidOperationException(); } @@ -245,7 +248,7 @@ public IeJsContext CreateContext(IDebugApplication32 debugApplication) public IeJsContext CreateContext() { IeJsContext reference; - IeJsErrorHelpers.ThrowIfError(Environment.Is64BitProcess ? + IeJsErrorHelpers.ThrowIfError(Utils.Is64BitProcess() ? IeNativeMethods.JsCreateContext(this, (IDebugApplication64)null, out reference) : IeNativeMethods.JsCreateContext(this, (IDebugApplication32)null, out reference) diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/IeJsScope.cs b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsScope.cs index 756f2b2..fcc534a 100644 --- a/src/MsieJavaScriptEngine/JsRt/Ie/IeJsScope.cs +++ b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsScope.cs @@ -1,7 +1,7 @@ -namespace MsieJavaScriptEngine.JsRt.Ie -{ - using System; +using System; +namespace MsieJavaScriptEngine.JsRt.Ie +{ /// /// “IE” scope automatically sets a context to current and resets the original context /// when disposed @@ -16,7 +16,7 @@ internal struct IeJsScope : IDisposable /// /// Whether the structure has been disposed /// - private StatedFlag _disposedFlag; + private bool _disposed; /// @@ -25,7 +25,7 @@ internal struct IeJsScope : IDisposable /// The context to create the scope for public IeJsScope(IeJsContext context) { - _disposedFlag = new StatedFlag(); + _disposed = false; _previousContext = IeJsContext.Current; IeJsContext.Current = context; } @@ -37,10 +37,13 @@ public IeJsScope(IeJsContext context) /// public void Dispose() { - if (_disposedFlag.Set()) + if (_disposed) { - IeJsContext.Current = _previousContext; + return; } + + IeJsContext.Current = _previousContext; + _disposed = true; } #endregion diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/IeJsScriptException.cs b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsScriptException.cs index a560286..ec9cf60 100644 --- a/src/MsieJavaScriptEngine/JsRt/Ie/IeJsScriptException.cs +++ b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsScriptException.cs @@ -1,24 +1,30 @@ +#if !NETSTANDARD1_3 +using System; +using System.Runtime.Serialization; + +#endif namespace MsieJavaScriptEngine.JsRt.Ie { - using System; - using System.Runtime.Serialization; - /// /// IE script exception /// +#if !NETSTANDARD1_3 [Serializable] - internal sealed class IeJsScriptException : JsException +#endif + public sealed class IeJsScriptException : JsException { /// /// The error /// +#if !NETSTANDARD1_3 [NonSerialized] +#endif private readonly IeJsValue _error; /// /// Gets a JavaScript object representing the script error /// - public IeJsValue Error + internal IeJsValue Error { get { return _error; } } @@ -27,31 +33,45 @@ public IeJsValue Error /// /// Initializes a new instance of the class /// - /// The error code returned - /// The JavaScript error object - public IeJsScriptException(JsErrorCode code, IeJsValue error) - : this(code, error, "JavaScript Exception") + /// The error code returned + public IeJsScriptException(JsErrorCode errorCode) + : this(errorCode, "JavaScript Exception") + { } + + /// + /// Initializes a new instance of the class + /// + /// The error code returned + /// The error message + public IeJsScriptException(JsErrorCode errorCode, string message) + : this(errorCode, IeJsValue.Invalid, message) { } /// /// Initializes a new instance of the class + /// with a specified error message /// - /// The error code returned + /// The error code returned /// The JavaScript error object /// The error message - public IeJsScriptException(JsErrorCode code, IeJsValue error, string message) - : base(code, message) + internal IeJsScriptException(JsErrorCode errorCode, IeJsValue error, string message) + : base(errorCode, message) { _error = error; } +#if !NETSTANDARD1_3 /// - /// Initializes a new instance of the class + /// Initializes a new instance of the class with serialized data /// - /// The serialization info - /// The streaming context + /// The object that holds the serialized data + /// The contextual information about the source or destination +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif private IeJsScriptException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/IeJsValue.cs b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsValue.cs index 67e0d3f..bb6caa1 100644 --- a/src/MsieJavaScriptEngine/JsRt/Ie/IeJsValue.cs +++ b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsValue.cs @@ -1,14 +1,15 @@ -namespace MsieJavaScriptEngine.JsRt.Ie -{ - using System; - using System.Runtime.InteropServices; +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +namespace MsieJavaScriptEngine.JsRt.Ie +{ /// /// “IE” 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 IeJsValue { @@ -305,22 +306,6 @@ public static IeJsValue FromString(string value) return reference; } - /// - /// Creates a JavaScript value that is a projection of the passed in object - /// - /// - /// Requires an active script context. - /// - /// The object to be projected - /// The JavaScript value that is a projection of the object - public static IeJsValue FromObject(object value) - { - IeJsValue reference; - IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsVariantToValue(ref value, out reference)); - - return reference; - } - /// /// Creates a new Object /// @@ -342,10 +327,10 @@ public static IeJsValue CreateObject() /// /// Requires an active script context. /// - /// External data that the object will represent. May be null. - /// A callback for when the object is finalized. May be null. + /// External data that the object will represent. May be null. + /// A callback for when the object is finalized. May be null. /// The new Object - public static IeJsValue CreateExternalObject(IntPtr data, JsObjectFinalizeCallback finalizer) + public static IeJsValue CreateExternalObject(IntPtr data, JsFinalizeCallback finalizer) { IeJsValue reference; IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsCreateExternalObject(data, finalizer, out reference)); @@ -403,7 +388,7 @@ public static IeJsValue CreateArray(uint length) } /// - /// Creates a new JavaScript error object + /// Creates a new JavaScript Error object /// /// /// Requires an active script context. @@ -419,7 +404,7 @@ public static IeJsValue CreateError(IeJsValue message) } /// - /// Creates a new JavaScript RangeError error object + /// Creates a new JavaScript RangeError error object /// /// /// Requires an active script context. @@ -435,7 +420,7 @@ public static IeJsValue CreateRangeError(IeJsValue message) } /// - /// Creates a new JavaScript ReferenceError error object + /// Creates a new JavaScript ReferenceError error object /// /// /// Requires an active script context. @@ -451,7 +436,7 @@ public static IeJsValue CreateReferenceError(IeJsValue message) } /// - /// Creates a new JavaScript SyntaxError error object + /// Creates a new JavaScript SyntaxError error object /// /// /// Requires an active script context. @@ -467,7 +452,7 @@ public static IeJsValue CreateSyntaxError(IeJsValue message) } /// - /// Creates a new JavaScript TypeError error object + /// Creates a new JavaScript TypeError error object /// /// /// Requires an active script context. @@ -483,7 +468,7 @@ public static IeJsValue CreateTypeError(IeJsValue message) } /// - /// Creates a new JavaScript URIError error object + /// Creates a new JavaScript URIError error object /// /// /// Requires an active script context. @@ -503,8 +488,8 @@ public static IeJsValue CreateUriError(IeJsValue 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() @@ -519,7 +504,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() @@ -567,40 +552,46 @@ public double ToDouble() } /// - /// Retrieves a string pointer of a String value + /// Retrieves a int value of a Number value /// /// /// - /// This function retrieves the string pointer of a String value. It will fail with - /// InvalidArgument if the type of the value is not String. + /// 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 string - public new string ToString() + /// The int value + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + public int ToInt32() { - IntPtr buffer; - UIntPtr length; - IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsStringToPointer(this, out buffer, out length)); + int value = (int)ToDouble(); - return Marshal.PtrToStringAuto(buffer, (int)length); + return value; } /// - /// Retrieves a object representation of an Object value + /// Retrieves a string pointer of a String value /// /// + /// + /// This function retrieves the string pointer of a String value. It will fail with + /// InvalidArgument if the type of the value is not String. + /// + /// /// Requires an active script context. + /// /// - /// The object representation of the value - public object ToObject() + /// The string + public new string ToString() { - object value; - IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsValueToVariant(this, out value)); + IntPtr buffer; + UIntPtr length; + IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsStringToPointer(this, out buffer, out length)); - return value; + return Marshal.PtrToStringUni(buffer, (int)length); } /// @@ -847,7 +838,7 @@ public void DeleteIndexedProperty(IeJsValue index) /// /// /// - /// This function is equivalent to the "==" operator in JavaScript. + /// This function is equivalent to the == operator in JavaScript. /// /// /// Requires an active script context. @@ -868,7 +859,7 @@ public bool Equals(IeJsValue other) /// /// /// - /// This function is equivalent to the "===" operator in JavaScript. + /// This function is equivalent to the === operator in JavaScript. /// /// /// Requires an active script context. @@ -891,14 +882,14 @@ public bool StrictEquals(IeJsValue 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 IeJsValue CallFunction(params IeJsValue[] arguments) { IeJsValue returnReference; if (arguments.Length > ushort.MaxValue) { - throw new ArgumentOutOfRangeException("arguments"); + throw new ArgumentOutOfRangeException(nameof(arguments)); } IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsCallFunction(this, arguments, (ushort)arguments.Length, out returnReference)); @@ -913,14 +904,14 @@ public IeJsValue CallFunction(params IeJsValue[] 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 IeJsValue ConstructObject(params IeJsValue[] arguments) { IeJsValue returnReference; if (arguments.Length > ushort.MaxValue) { - throw new ArgumentOutOfRangeException("arguments"); + throw new ArgumentOutOfRangeException(nameof(arguments)); } IeJsErrorHelpers.ThrowIfError(IeNativeMethods.JsConstructObject(this, arguments, (ushort)arguments.Length, out returnReference)); diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/IeJsValueExtensions.cs b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsValueExtensions.cs new file mode 100644 index 0000000..97cc3a2 --- /dev/null +++ b/src/MsieJavaScriptEngine/JsRt/Ie/IeJsValueExtensions.cs @@ -0,0 +1,111 @@ +namespace MsieJavaScriptEngine.JsRt.Ie +{ + /// + /// Extensions for the “IE” JavaScript value + /// + internal static class IeJsValueExtensions + { + /// + /// Gets a property descriptor for an object's own property + /// + /// + /// Requires an active script context. + /// + /// The JavaScript value + /// The name of the property + /// The property descriptor + public static IeJsValue GetOwnPropertyDescriptor(this IeJsValue source, string propertyName) + { + IeJsPropertyId propertyId = IeJsPropertyId.FromString(propertyName); + IeJsValue resultValue = source.GetOwnPropertyDescriptor(propertyId); + + return resultValue; + } + + /// + /// Determines whether an object has a property + /// + /// + /// Requires an active script context. + /// + /// The JavaScript value + /// The name of the property + /// Whether the object (or a prototype) has the property + public static bool HasProperty(this IeJsValue source, string propertyName) + { + IeJsPropertyId propertyId = IeJsPropertyId.FromString(propertyName); + bool result = source.HasProperty(propertyId); + + return result; + } + + /// + /// Gets an object's property + /// + /// + /// Requires an active script context. + /// + /// The JavaScript value + /// The name of the property + /// The value of the property + public static IeJsValue GetProperty(this IeJsValue source, string name) + { + IeJsPropertyId id = IeJsPropertyId.FromString(name); + IeJsValue resultValue = source.GetProperty(id); + + return resultValue; + } + + /// + /// Sets an object's property + /// + /// + /// Requires an active script context. + /// + /// The JavaScript value + /// The name of the property + /// The new value of the property + /// The property set should follow strict mode rules + public static void SetProperty(this IeJsValue source, string name, IeJsValue value, bool useStrictRules) + { + IeJsPropertyId id = IeJsPropertyId.FromString(name); + source.SetProperty(id, value, useStrictRules); + } + + /// + /// Deletes an object's property + /// + /// + /// Requires an active script context. + /// + /// The JavaScript value + /// The name of the property + /// The property set should follow strict mode rules + /// Whether the property was deleted + public static IeJsValue DeleteProperty(this IeJsValue source, string propertyName, bool useStrictRules) + { + IeJsPropertyId propertyId = IeJsPropertyId.FromString(propertyName); + IeJsValue resultValue = source.DeleteProperty(propertyId, useStrictRules); + + return resultValue; + } + + /// + /// Defines a new object's own property from a property descriptor + /// + /// + /// Requires an active script context. + /// + /// The JavaScript value + /// The name of the property + /// The property descriptor + /// Whether the property was defined + public static bool DefineProperty(this IeJsValue source, string propertyName, IeJsValue propertyDescriptor) + { + IeJsPropertyId propertyId = IeJsPropertyId.FromString(propertyName); + bool result = source.DefineProperty(propertyId, propertyDescriptor); + + return result; + } + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/IeNativeMethods.cs b/src/MsieJavaScriptEngine/JsRt/Ie/IeNativeMethods.cs index 43885fd..4b9aabd 100644 --- a/src/MsieJavaScriptEngine/JsRt/Ie/IeNativeMethods.cs +++ b/src/MsieJavaScriptEngine/JsRt/Ie/IeNativeMethods.cs @@ -1,281 +1,289 @@ -namespace MsieJavaScriptEngine.JsRt.Ie -{ - using System; - using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; - using Constants; - using JsRt; +using MsieJavaScriptEngine.ActiveScript.Debugging; +using MsieJavaScriptEngine.Constants; +namespace MsieJavaScriptEngine.JsRt.Ie +{ /// /// “IE” native methods /// internal static class IeNativeMethods { - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsCreateRuntime(JsRuntimeAttributes attributes, JsRuntimeVersion runtimeVersion, JsThreadServiceCallback threadService, out IeJsRuntime runtime); + #region Hosting [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsCollectGarbage(IeJsRuntime handle); + internal static extern JsErrorCode JsParseScript(string script, JsSourceContext sourceContext, + string sourceUrl, out IeJsValue result); [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsDisposeRuntime(IeJsRuntime handle); + internal static extern JsErrorCode JsRunScript(string script, JsSourceContext sourceContext, + string sourceUrl, out IeJsValue result); [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsGetRuntimeMemoryUsage(IeJsRuntime runtime, out UIntPtr memoryUsage); + internal static extern JsErrorCode JsSerializeScript(string script, byte[] buffer, ref uint bufferSize); [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsGetRuntimeMemoryLimit(IeJsRuntime runtime, out UIntPtr memoryLimit); + internal static extern JsErrorCode JsParseSerializedScript(string script, byte[] buffer, + JsSourceContext sourceContext, string sourceUrl, out IeJsValue result); [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsSetRuntimeMemoryLimit(IeJsRuntime runtime, UIntPtr memoryLimit); + internal static extern JsErrorCode JsRunSerializedScript(string script, byte[] buffer, + JsSourceContext sourceContext, string sourceUrl, out IeJsValue result); [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsSetRuntimeMemoryAllocationCallback(IeJsRuntime runtime, IntPtr callbackState, JsMemoryAllocationCallback allocationCallback); + internal static extern JsErrorCode JsGetPropertyIdFromName(string name, out IeJsPropertyId propertyId); + + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsGetPropertyNameFromId(IeJsPropertyId propertyId, out IntPtr buffer); [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsSetRuntimeBeforeCollectCallback(IeJsRuntime runtime, IntPtr callbackState, JsBeforeCollectCallback beforeCollectCallback); + internal static extern JsErrorCode JsPointerToString(string value, UIntPtr stringLength, + out IeJsValue stringValue); + + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsStringToPointer(IeJsValue value, out IntPtr stringValue, + out UIntPtr stringLength); + + #endregion + + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsCreateRuntime(JsRuntimeAttributes attributes, + JsRuntimeVersion runtimeVersion, JsThreadServiceCallback threadService, out IeJsRuntime runtime); + + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsCollectGarbage(IeJsRuntime handle); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode, EntryPoint = "JsAddRef")] + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsDisposeRuntime(IeJsRuntime handle); + + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsGetRuntimeMemoryUsage(IeJsRuntime runtime, out UIntPtr memoryUsage); + + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsGetRuntimeMemoryLimit(IeJsRuntime runtime, out UIntPtr memoryLimit); + + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsSetRuntimeMemoryLimit(IeJsRuntime runtime, UIntPtr memoryLimit); + + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsSetRuntimeMemoryAllocationCallback(IeJsRuntime runtime, + IntPtr callbackState, JsMemoryAllocationCallback allocationCallback); + + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsSetRuntimeBeforeCollectCallback(IeJsRuntime runtime, + IntPtr callbackState, JsBeforeCollectCallback beforeCollectCallback); + + [DllImport(DllName.JScript9, EntryPoint = "JsAddRef")] internal static extern JsErrorCode JsContextAddRef(IeJsContext reference, out uint count); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsAddRef(IeJsValue reference, out uint count); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode, EntryPoint = "JsRelease")] + [DllImport(DllName.JScript9, EntryPoint = "JsRelease")] internal static extern JsErrorCode JsContextRelease(IeJsContext reference, out uint count); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsRelease(IeJsValue reference, out uint count); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsCreateContext(IeJsRuntime runtime, IDebugApplication64 debugSite, out IeJsContext newContext); + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsCreateContext(IeJsRuntime runtime, IDebugApplication64 debugSite, + out IeJsContext newContext); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsCreateContext(IeJsRuntime runtime, IDebugApplication32 debugSite, out IeJsContext newContext); + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsCreateContext(IeJsRuntime runtime, IDebugApplication32 debugSite, + out IeJsContext newContext); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsGetCurrentContext(out IeJsContext currentContext); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsSetCurrentContext(IeJsContext context); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsGetRuntime(IeJsContext context, out IeJsRuntime runtime); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsStartDebugging(IDebugApplication64 debugApplication); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsStartDebugging(IDebugApplication32 debugApplication); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsIdle(out uint nextIdleTick); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsParseScript(string script, JsSourceContext sourceContext, string sourceUrl, out IeJsValue result); - - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsRunScript(string script, JsSourceContext sourceContext, string sourceUrl, out IeJsValue result); - - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsSerializeScript(string script, byte[] buffer, ref ulong bufferSize); - - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsParseSerializedScript(string script, byte[] buffer, JsSourceContext sourceContext, string sourceUrl, out IeJsValue result); - - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsRunSerializedScript(string script, byte[] buffer, JsSourceContext sourceContext, string sourceUrl, out IeJsValue result); - - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsGetPropertyIdFromName(string name, out IeJsPropertyId propertyId); - - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsGetPropertyNameFromId(IeJsPropertyId propertyId, out IntPtr buffer); - - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsGetUndefinedValue(out IeJsValue undefinedValue); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsGetNullValue(out IeJsValue nullValue); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsGetTrueValue(out IeJsValue trueValue); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsGetFalseValue(out IeJsValue falseValue); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsBoolToBoolean(bool value, out IeJsValue booleanValue); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsBooleanToBool(IeJsValue booleanValue, out bool boolValue); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsConvertValueToBoolean(IeJsValue value, out IeJsValue booleanValue); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsGetValueType(IeJsValue value, out JsValueType type); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsDoubleToNumber(double doubleValue, out IeJsValue value); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsIntToNumber(int intValue, out IeJsValue value); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsNumberToDouble(IeJsValue value, out double doubleValue); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsConvertValueToNumber(IeJsValue value, out IeJsValue numberValue); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsGetStringLength(IeJsValue sringValue, out int length); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsPointerToString(string value, UIntPtr stringLength, out IeJsValue stringValue); - - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsStringToPointer(IeJsValue value, out IntPtr stringValue, out UIntPtr stringLength); - - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsConvertValueToString(IeJsValue value, out IeJsValue stringValue); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsVariantToValue([MarshalAs(UnmanagedType.Struct)] ref object var, out IeJsValue value); - - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsValueToVariant(IeJsValue obj, [MarshalAs(UnmanagedType.Struct)] out object var); - - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsGetGlobalObject(out IeJsValue globalObject); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsCreateObject(out IeJsValue obj); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsCreateExternalObject(IntPtr data, JsObjectFinalizeCallback finalizeCallback, out IeJsValue obj); + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsCreateExternalObject(IntPtr data, + JsFinalizeCallback finalizeCallback, out IeJsValue obj); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsConvertValueToObject(IeJsValue value, out IeJsValue obj); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsGetPrototype(IeJsValue obj, out IeJsValue prototypeObject); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsSetPrototype(IeJsValue obj, IeJsValue prototypeObject); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsGetExtensionAllowed(IeJsValue obj, out bool value); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsPreventExtension(IeJsValue obj); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsGetProperty(IeJsValue obj, IeJsPropertyId propertyId, out IeJsValue value); + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsGetProperty(IeJsValue obj, IeJsPropertyId propertyId, + out IeJsValue value); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsGetOwnPropertyDescriptor(IeJsValue obj, IeJsPropertyId propertyId, out IeJsValue propertyDescriptor); + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsGetOwnPropertyDescriptor(IeJsValue obj, IeJsPropertyId propertyId, + out IeJsValue propertyDescriptor); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsGetOwnPropertyNames(IeJsValue obj, out IeJsValue propertyNames); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsSetProperty(IeJsValue obj, IeJsPropertyId propertyId, IeJsValue value, bool useStrictRules); + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsSetProperty(IeJsValue obj, IeJsPropertyId propertyId, IeJsValue value, + bool useStrictRules); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsHasProperty(IeJsValue obj, IeJsPropertyId propertyId, out bool hasProperty); + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsHasProperty(IeJsValue obj, IeJsPropertyId propertyId, + out bool hasProperty); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsDeleteProperty(IeJsValue obj, IeJsPropertyId propertyId, bool useStrictRules, out IeJsValue result); + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsDeleteProperty(IeJsValue obj, IeJsPropertyId propertyId, + bool useStrictRules, out IeJsValue result); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsDefineProperty(IeJsValue obj, IeJsPropertyId propertyId, IeJsValue propertyDescriptor, out bool result); + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsDefineProperty(IeJsValue obj, IeJsPropertyId propertyId, + IeJsValue propertyDescriptor, out bool result); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsHasIndexedProperty(IeJsValue obj, IeJsValue index, out bool result); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsGetIndexedProperty(IeJsValue obj, IeJsValue index, out IeJsValue result); + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsGetIndexedProperty(IeJsValue obj, IeJsValue index, + out IeJsValue result); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsSetIndexedProperty(IeJsValue obj, IeJsValue index, IeJsValue value); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsDeleteIndexedProperty(IeJsValue obj, IeJsValue index); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsEquals(IeJsValue obj1, IeJsValue obj2, out bool result); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsStrictEquals(IeJsValue obj1, IeJsValue obj2, out bool result); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsHasExternalData(IeJsValue obj, out bool value); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsGetExternalData(IeJsValue obj, out IntPtr externalData); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsSetExternalData(IeJsValue obj, IntPtr externalData); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsCreateArray(uint length, out IeJsValue result); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsCallFunction(IeJsValue function, IeJsValue[] arguments, ushort argumentCount, out IeJsValue result); + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsCallFunction(IeJsValue function, IeJsValue[] arguments, + ushort argumentCount, out IeJsValue result); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsConstructObject(IeJsValue function, IeJsValue[] arguments, ushort argumentCount, out IeJsValue result); + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsConstructObject(IeJsValue function, IeJsValue[] arguments, + ushort argumentCount, out IeJsValue result); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsCreateFunction(IeJsNativeFunction nativeFunction, IntPtr externalData, out IeJsValue function); + [DllImport(DllName.JScript9)] + internal static extern JsErrorCode JsCreateFunction(IeJsNativeFunction nativeFunction, + IntPtr externalData, out IeJsValue function); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsCreateError(IeJsValue message, out IeJsValue error); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsCreateRangeError(IeJsValue message, out IeJsValue error); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsCreateReferenceError(IeJsValue message, out IeJsValue error); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsCreateSyntaxError(IeJsValue message, out IeJsValue error); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsCreateTypeError(IeJsValue message, out IeJsValue error); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsCreateURIError(IeJsValue message, out IeJsValue error); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsHasException(out bool hasException); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsGetAndClearException(out IeJsValue exception); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsSetException(IeJsValue exception); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsDisableRuntimeExecution(IeJsRuntime runtime); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsEnableRuntimeExecution(IeJsRuntime runtime); - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] + [DllImport(DllName.JScript9)] internal static extern JsErrorCode JsIsRuntimeExecutionDisabled(IeJsRuntime runtime, out bool isDisabled); - - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsStartProfiling(IActiveScriptProfilerCallback callback, ProfilerEventMask eventMask, int context); - - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsStopProfiling(int reason); - - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsEnumerateHeap(out IActiveScriptProfilerHeapEnum enumerator); - - [DllImport(DllName.JScript9, CharSet = CharSet.Unicode)] - internal static extern JsErrorCode JsIsEnumeratingHeap(out bool isEnumeratingHeap); } } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/Ie/IeTypeMapper.cs b/src/MsieJavaScriptEngine/JsRt/Ie/IeTypeMapper.cs new file mode 100644 index 0000000..3bc9ce7 --- /dev/null +++ b/src/MsieJavaScriptEngine/JsRt/Ie/IeTypeMapper.cs @@ -0,0 +1,707 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using MsieJavaScriptEngine.Extensions; +using MsieJavaScriptEngine.Helpers; +using MsieJavaScriptEngine.JsRt.Embedding; +using MsieJavaScriptEngine.Resources; + +using WrapperException = MsieJavaScriptEngine.JsException; + +namespace MsieJavaScriptEngine.JsRt.Ie +{ + using IeEmbeddedItem = EmbeddedItem; + using IeEmbeddedObject = EmbeddedObject; + using IeEmbeddedType = EmbeddedType; + + /// + /// “IE” type mapper + /// + internal sealed class IeTypeMapper : TypeMapper + { + /// + /// Constructs an instance of the “IE” type mapper + /// + /// Flag for whether to allow the usage of reflection API in the script code + public IeTypeMapper(bool allowReflection) + : base(allowReflection) + { } + + + /// + /// Makes a mapping of value from the host type to a script type + /// + /// The source value + /// The mapped value + public override IeJsValue MapToScriptType(object value) + { + if (value is null) + { + return IeJsValue.Null; + } + + if (value is Undefined) + { + return IeJsValue.Undefined; + } + + var typeCode = value.GetType().GetTypeCode(); + + switch (typeCode) + { + case TypeCode.Boolean: + return (bool)value ? IeJsValue.True : IeJsValue.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 IeJsValue.FromInt32(Convert.ToInt32(value)); + + case TypeCode.Single: + case TypeCode.Double: + case TypeCode.Decimal: + return IeJsValue.FromDouble(Convert.ToDouble(value)); + + case TypeCode.Char: + case TypeCode.String: + return IeJsValue.FromString((string)value); + + default: + return value is IeJsValue ? (IeJsValue)value : GetOrCreateScriptObject(value); + } + } + + /// + /// Makes a mapping of value from the script type to a host type + /// + /// The source value + /// The mapped value + public override object MapToHostType(IeJsValue 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: + IeJsPropertyId externalObjectPropertyId = IeJsPropertyId.FromString(ExternalObjectPropertyName); + if (value.HasProperty(externalObjectPropertyId)) + { + IeJsValue 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: + result = value.HasExternalData ? + GCHandle.FromIntPtr(value.ExternalData).Target : value.ConvertToObject(); + break; + default: + throw new ArgumentOutOfRangeException(); + } + + return result; + } + + protected override IeEmbeddedObject CreateEmbeddedObjectOrFunction(object obj) + { + var del = obj as Delegate; + IeEmbeddedObject embeddedObject = del is not null ? + CreateEmbeddedFunction(del) : CreateEmbeddedObject(obj); + + return embeddedObject; + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private IeEmbeddedObject CreateEmbeddedObject(object obj) + { + GCHandle objHandle = GCHandle.Alloc(obj); + IntPtr objPtr = GCHandle.ToIntPtr(objHandle); + IeJsValue objValue = IeJsValue.CreateExternalObject(objPtr, _embeddedObjectFinalizeCallback); + + var embeddedObject = new IeEmbeddedObject(obj, objValue); + + ProjectFields(embeddedObject); + ProjectProperties(embeddedObject); + ProjectMethods(embeddedObject); + FreezeObject(objValue); + + return embeddedObject; + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private IeEmbeddedObject CreateEmbeddedFunction(Delegate del) + { + IeJsNativeFunction 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) + { + IeJsValue undefinedValue = IeJsValue.Undefined; + Exception exception = UnwrapException(e); + var wrapperException = exception as WrapperException; + IeJsValue errorValue = wrapperException is not null ? + CreateErrorFromWrapperException(wrapperException) + : + IeJsErrorHelpers.CreateError(string.Format( + CommonStrings.Runtime_HostDelegateInvocationFailed, exception.Message)) + ; + IeJsContext.SetException(errorValue); + + return undefinedValue; + } + + IeJsValue resultValue = MapToScriptType(result); + + return resultValue; + }; + + GCHandle delHandle = GCHandle.Alloc(del); + IntPtr delPtr = GCHandle.ToIntPtr(delHandle); + IeJsValue objValue = IeJsValue.CreateExternalObject(delPtr, _embeddedObjectFinalizeCallback); + + IeJsValue functionValue = IeJsValue.CreateFunction(nativeFunction); + SetNonEnumerableProperty(functionValue, ExternalObjectPropertyName, objValue); + + var embeddedObject = new IeEmbeddedObject(del, functionValue, [nativeFunction]); + + return embeddedObject; + } + + protected override IeEmbeddedType 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); + + IeJsNativeFunction nativeConstructorFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + object result; + IeJsValue resultValue; + object[] processedArgs = GetHostItemMemberArguments(args); + + if (processedArgs.Length == 0 && typeInfo.IsValueType) + { + result = Activator.CreateInstance(type); + resultValue = MapToScriptType(result); + + return resultValue; + } + + IeJsValue undefinedValue = IeJsValue.Undefined; + + if (constructors.Length == 0) + { + CreateAndSetError(string.Format(CommonStrings.Runtime_HostTypeConstructorNotFound, typeName)); + return undefinedValue; + } + + var bestFitConstructor = (ConstructorInfo)ReflectionHelpers.GetBestFitMethod( + constructors, processedArgs); + if (bestFitConstructor is null) + { + CreateAndSetReferenceError(string.Format( + CommonStrings.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; + IeJsValue errorValue = wrapperException is not null ? + CreateErrorFromWrapperException(wrapperException) + : + IeJsErrorHelpers.CreateError(string.Format( + CommonStrings.Runtime_HostTypeConstructorInvocationFailed, typeName, exception.Message)) + ; + IeJsContext.SetException(errorValue); + + return undefinedValue; + } + + resultValue = MapToScriptType(result); + + return resultValue; + }; + + GCHandle embeddedTypeHandle = GCHandle.Alloc(type); + IntPtr embeddedTypePtr = GCHandle.ToIntPtr(embeddedTypeHandle); + IeJsValue objValue = IeJsValue.CreateExternalObject(embeddedTypePtr, + _embeddedTypeFinalizeCallback); + + IeJsValue typeValue = IeJsValue.CreateFunction(nativeConstructorFunction); + SetNonEnumerableProperty(typeValue, ExternalObjectPropertyName, objValue); + + var embeddedType = new IeEmbeddedType(type, typeValue, + new List { nativeConstructorFunction }); + + ProjectFields(embeddedType); + ProjectProperties(embeddedType); + ProjectMethods(embeddedType); + FreezeObject(typeValue); + + return embeddedType; + } + + private void ProjectFields(IeEmbeddedItem externalItem) + { + Type type = externalItem.HostType; + object obj = externalItem.HostObject; + IeJsValue 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; + + IeJsValue descriptorValue = IeJsValue.CreateObject(); + descriptorValue.SetProperty("enumerable", IeJsValue.True, true); + + IeJsNativeFunction nativeGetFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + IeJsValue undefinedValue = IeJsValue.Undefined; + + if (instance && obj is null) + { + CreateAndSetTypeError(string.Format( + CommonStrings.Runtime_InvalidThisContextForHostObjectField, fieldName)); + return undefinedValue; + } + + object result; + + try + { + result = field.GetValue(obj); + } + catch (Exception e) + { + Exception exception = UnwrapException(e); + var wrapperException = exception as WrapperException; + IeJsValue errorValue; + + if (wrapperException is not null) + { + errorValue = CreateErrorFromWrapperException(wrapperException); + } + else + { + string errorMessage = instance ? + string.Format(CommonStrings.Runtime_HostObjectFieldGettingFailed, fieldName, + exception.Message) + : + string.Format(CommonStrings.Runtime_HostTypeFieldGettingFailed, fieldName, typeName, + exception.Message) + ; + errorValue = IeJsErrorHelpers.CreateError(errorMessage); + } + IeJsContext.SetException(errorValue); + + return undefinedValue; + } + + IeJsValue resultValue = MapToScriptType(result); + + return resultValue; + }; + nativeFunctions.Add(nativeGetFunction); + + IeJsValue getMethodValue = IeJsValue.CreateFunction(nativeGetFunction); + descriptorValue.SetProperty("get", getMethodValue, true); + + if (!field.IsInitOnly) + { + IeJsNativeFunction nativeSetFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + IeJsValue undefinedValue = IeJsValue.Undefined; + + if (instance && obj is null) + { + CreateAndSetTypeError(string.Format( + CommonStrings.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; + IeJsValue errorValue; + + if (wrapperException is not null) + { + errorValue = CreateErrorFromWrapperException(wrapperException); + } + else + { + string errorMessage = instance ? + string.Format(CommonStrings.Runtime_HostObjectFieldSettingFailed, fieldName, + exception.Message) + : + string.Format(CommonStrings.Runtime_HostTypeFieldSettingFailed, fieldName, typeName, + exception.Message) + ; + errorValue = IeJsErrorHelpers.CreateError(errorMessage); + } + IeJsContext.SetException(errorValue); + + return undefinedValue; + } + + return undefinedValue; + }; + nativeFunctions.Add(nativeSetFunction); + + IeJsValue setMethodValue = IeJsValue.CreateFunction(nativeSetFunction); + descriptorValue.SetProperty("set", setMethodValue, true); + } + + typeValue.DefineProperty(fieldName, descriptorValue); + } + } + + private void ProjectProperties(IeEmbeddedItem externalItem) + { + Type type = externalItem.HostType; + object obj = externalItem.HostObject; + IeJsValue 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; + + IeJsValue descriptorValue = IeJsValue.CreateObject(); + descriptorValue.SetProperty("enumerable", IeJsValue.True, true); + + if (property.CanRead) + { + IeJsNativeFunction nativeGetFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + IeJsValue undefinedValue = IeJsValue.Undefined; + + if (instance && obj is null) + { + CreateAndSetTypeError(string.Format( + CommonStrings.Runtime_InvalidThisContextForHostObjectProperty, propertyName)); + return undefinedValue; + } + + object result; + + try + { + result = property.GetValue(obj, []); + } + catch (Exception e) + { + Exception exception = UnwrapException(e); + var wrapperException = exception as WrapperException; + IeJsValue errorValue; + + if (wrapperException is not null) + { + errorValue = CreateErrorFromWrapperException(wrapperException); + } + else + { + string errorMessage = instance ? + string.Format(CommonStrings.Runtime_HostObjectPropertyGettingFailed, propertyName, + exception.Message) + : + string.Format(CommonStrings.Runtime_HostTypePropertyGettingFailed, propertyName, + typeName, exception.Message) + ; + errorValue = IeJsErrorHelpers.CreateError(errorMessage); + } + IeJsContext.SetException(errorValue); + + return undefinedValue; + } + + IeJsValue resultValue = MapToScriptType(result); + + return resultValue; + }; + nativeFunctions.Add(nativeGetFunction); + + IeJsValue getMethodValue = IeJsValue.CreateFunction(nativeGetFunction); + descriptorValue.SetProperty("get", getMethodValue, true); + } + + if (property.CanWrite) + { + IeJsNativeFunction nativeSetFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + IeJsValue undefinedValue = IeJsValue.Undefined; + + if (instance && obj is null) + { + CreateAndSetTypeError(string.Format( + CommonStrings.Runtime_InvalidThisContextForHostObjectProperty, propertyName)); + return undefinedValue; + } + + object value = MapToHostType(args[1]); + ReflectionHelpers.FixPropertyValueType(ref value, property); + + try + { + property.SetValue(obj, value, []); + } + catch (Exception e) + { + Exception exception = UnwrapException(e); + var wrapperException = exception as WrapperException; + IeJsValue errorValue; + + if (wrapperException is not null) + { + errorValue = CreateErrorFromWrapperException(wrapperException); + } + else + { + string errorMessage = instance ? + string.Format(CommonStrings.Runtime_HostObjectPropertySettingFailed, propertyName, + exception.Message) + : + string.Format(CommonStrings.Runtime_HostTypePropertySettingFailed, propertyName, + typeName, exception.Message) + ; + errorValue = IeJsErrorHelpers.CreateError(errorMessage); + } + IeJsContext.SetException(errorValue); + + return undefinedValue; + } + + return undefinedValue; + }; + nativeFunctions.Add(nativeSetFunction); + + IeJsValue setMethodValue = IeJsValue.CreateFunction(nativeSetFunction); + descriptorValue.SetProperty("set", setMethodValue, true); + } + + typeValue.DefineProperty(propertyName, descriptorValue); + } + } + + private void ProjectMethods(IeEmbeddedItem externalItem) + { + Type type = externalItem.HostType; + object obj = externalItem.HostObject; + IeJsValue 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(); + + IeJsNativeFunction nativeFunction = (callee, isConstructCall, args, argCount, callbackData) => + { + IeJsValue undefinedValue = IeJsValue.Undefined; + + if (instance && obj is null) + { + CreateAndSetTypeError(string.Format( + CommonStrings.Runtime_InvalidThisContextForHostObjectMethod, methodName)); + return undefinedValue; + } + + object[] processedArgs = GetHostItemMemberArguments(args); + + var bestFitMethod = (MethodInfo)ReflectionHelpers.GetBestFitMethod( + methodCandidates, processedArgs); + if (bestFitMethod is null) + { + CreateAndSetReferenceError(string.Format( + CommonStrings.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; + IeJsValue errorValue; + + if (wrapperException is not null) + { + errorValue = CreateErrorFromWrapperException(wrapperException); + } + else + { + string errorMessage = instance ? + string.Format(CommonStrings.Runtime_HostObjectMethodInvocationFailed, methodName, + exception.Message) + : + string.Format(CommonStrings.Runtime_HostTypeMethodInvocationFailed, methodName, + typeName, exception.Message) + ; + errorValue = IeJsErrorHelpers.CreateError(errorMessage); + } + IeJsContext.SetException(errorValue); + + return undefinedValue; + } + + IeJsValue resultValue = MapToScriptType(result); + + return resultValue; + }; + nativeFunctions.Add(nativeFunction); + + IeJsValue methodValue = IeJsValue.CreateFunction(nativeFunction); + typeValue.SetProperty(methodName, methodValue, true); + } + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static void FreezeObject(IeJsValue objValue) + { + IeJsValue objectValue = IeJsValue.GlobalObject.GetProperty("Object"); + IeJsValue freezeMethodValue = objectValue.GetProperty("freeze"); + + freezeMethodValue.CallFunction(objectValue, objValue); + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static void SetNonEnumerableProperty(IeJsValue objValue, string name, IeJsValue value) + { + IeJsValue descriptorValue = IeJsValue.CreateObject(); + descriptorValue.SetProperty("enumerable", IeJsValue.False, true); + descriptorValue.SetProperty("writable", IeJsValue.True, true); + + IeJsPropertyId id = IeJsPropertyId.FromString(name); + objValue.DefineProperty(id, descriptorValue); + objValue.SetProperty(id, value, true); + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static void CreateAndSetError(string message) + { + IeJsValue errorValue = IeJsErrorHelpers.CreateError(message); + IeJsContext.SetException(errorValue); + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static void CreateAndSetReferenceError(string message) + { + IeJsValue errorValue = IeJsErrorHelpers.CreateReferenceError(message); + IeJsContext.SetException(errorValue); + } + + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + private static void CreateAndSetTypeError(string message) + { + IeJsValue errorValue = IeJsErrorHelpers.CreateTypeError(message); + IeJsContext.SetException(errorValue); + } + + private static IeJsValue CreateErrorFromWrapperException(WrapperException exception) + { + var originalException = exception.InnerException as JsException; + var originalScriptException = originalException as IeJsScriptException; + IeJsValue errorValue = originalScriptException is not null ? + originalScriptException.Error + : + IeJsErrorHelpers.CreateError(exception.Description) + ; + + return errorValue; + } + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/JsBackgroundWorkItemCallback.cs b/src/MsieJavaScriptEngine/JsRt/JsBackgroundWorkItemCallback.cs index df7a572..759b7ac 100644 --- a/src/MsieJavaScriptEngine/JsRt/JsBackgroundWorkItemCallback.cs +++ b/src/MsieJavaScriptEngine/JsRt/JsBackgroundWorkItemCallback.cs @@ -1,7 +1,7 @@ -namespace MsieJavaScriptEngine.JsRt -{ - using System; +using System; +namespace MsieJavaScriptEngine.JsRt +{ /// /// The background work item callback /// diff --git a/src/MsieJavaScriptEngine/JsRt/JsBeforeCollectCallback.cs b/src/MsieJavaScriptEngine/JsRt/JsBeforeCollectCallback.cs index 0dc374f..a193022 100644 --- a/src/MsieJavaScriptEngine/JsRt/JsBeforeCollectCallback.cs +++ b/src/MsieJavaScriptEngine/JsRt/JsBeforeCollectCallback.cs @@ -1,10 +1,10 @@ -namespace MsieJavaScriptEngine.JsRt -{ - using System; +using System; +namespace MsieJavaScriptEngine.JsRt +{ /// /// The callback called before collection /// - /// The state passed to SetBeforeCollectCallback + /// The state passed to SetBeforeCollectCallback internal delegate void JsBeforeCollectCallback(IntPtr callbackState); } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/JsEngineException.cs b/src/MsieJavaScriptEngine/JsRt/JsEngineException.cs index 19eafd6..4167b86 100644 --- a/src/MsieJavaScriptEngine/JsRt/JsEngineException.cs +++ b/src/MsieJavaScriptEngine/JsRt/JsEngineException.cs @@ -1,38 +1,48 @@ +#if !NETSTANDARD1_3 +using System; +using System.Runtime.Serialization; + +#endif namespace MsieJavaScriptEngine.JsRt { - using System; - using System.Runtime.Serialization; - /// - /// The exception that occurred in the workings of the JavaScript engine itself + /// The exception that occurred in the workings of the JS engine itself /// +#if !NETSTANDARD1_3 [Serializable] - internal sealed class JsEngineException : JsException +#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 + /// Initializes a new instance of the class with serialized data /// - /// The serialization info - /// The streaming context + /// The object that holds the serialized data + /// The contextual information about the source or destination +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif private JsEngineException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/JsErrorCode.cs b/src/MsieJavaScriptEngine/JsRt/JsErrorCode.cs index f4719bb..b7ad833 100644 --- a/src/MsieJavaScriptEngine/JsRt/JsErrorCode.cs +++ b/src/MsieJavaScriptEngine/JsRt/JsErrorCode.cs @@ -1,17 +1,19 @@ -namespace MsieJavaScriptEngine.JsRt -{ - using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; +namespace MsieJavaScriptEngine.JsRt +{ /// /// 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 /// @@ -23,7 +25,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, @@ -75,7 +77,7 @@ internal enum JsErrorCode : uint HeapEnumInProgress, /// - /// A hosting API that operates on Object values was called with a non-Object value + /// A hosting API that operates on object values was called with a non-object value /// ArgumentNotObject, @@ -109,6 +111,10 @@ internal enum JsErrorCode : uint /// IdleNotEnabled, + #endregion + + #region Engine + /// /// Category of errors that relates to errors occurring within the engine itself /// @@ -119,6 +125,10 @@ internal enum JsErrorCode : uint /// OutOfMemory, + #endregion + + #region Script + /// /// Category of errors that relates to errors in a script /// @@ -140,11 +150,15 @@ internal enum JsErrorCode : uint ScriptTerminated, /// - /// A script was terminated because it tried to use "eval" or "function" and eval was disabled + /// A script was terminated because it tried to use eval or Function and eval was disabled /// [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Eval is a valid function name.")] ScriptEvalDisabled, + #endregion + + #region Fatal + /// /// Category of errors that are fatal and signify failure of the engine /// @@ -154,5 +168,7 @@ internal enum JsErrorCode : uint /// A fatal error in the engine has occurred /// Fatal + + #endregion } } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/JsException.cs b/src/MsieJavaScriptEngine/JsRt/JsException.cs index 78393ef..c2da3e4 100644 --- a/src/MsieJavaScriptEngine/JsRt/JsException.cs +++ b/src/MsieJavaScriptEngine/JsRt/JsException.cs @@ -1,71 +1,98 @@ +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#if !NET10_0_OR_GREATER +using System.Security.Permissions; +#endif +#endif + namespace MsieJavaScriptEngine.JsRt { - using System; - using System.Runtime.Serialization; - /// /// The exception returned from the Chakra engine /// +#if !NETSTANDARD1_3 [Serializable] - internal class JsException : Exception +#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 serialization info - /// The streaming context + /// The object that holds the serialized data + /// The contextual information about the source or destination +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif protected JsException(SerializationInfo info, StreamingContext context) : base(info, context) { - if (info != null) + if (info is not null) { - _code = (JsErrorCode) info.GetUInt32("code"); + _errorCode = (JsErrorCode)info.GetUInt32("ErrorCode"); } } + #region Exception overrides + /// - /// Serializes the exception information + /// Populates a with the data needed to serialize the target object /// - /// The serialization information - /// The streaming context + /// The to populate with data + /// The destination (see ) for this serialization +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#else + [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] +#endif public override void GetObjectData(SerializationInfo info, StreamingContext context) { + if (info is null) + { + throw new ArgumentNullException(nameof(info)); + } + base.GetObjectData(info, context); - info.AddValue("code", (uint)_code); + info.AddValue("ErrorCode", (uint)_errorCode); } + + #endregion +#endif } } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/JsFatalException.cs b/src/MsieJavaScriptEngine/JsRt/JsFatalException.cs index 82680e7..6898a4c 100644 --- a/src/MsieJavaScriptEngine/JsRt/JsFatalException.cs +++ b/src/MsieJavaScriptEngine/JsRt/JsFatalException.cs @@ -1,38 +1,48 @@ -namespace MsieJavaScriptEngine.JsRt -{ - using System; - using System.Runtime.Serialization; +#if !NETSTANDARD1_3 +using System; +using System.Runtime.Serialization; +#endif +namespace MsieJavaScriptEngine.JsRt +{ /// - /// The fatal exception + /// The fatal exception occurred /// +#if !NETSTANDARD1_3 [Serializable] - internal sealed class JsFatalException : JsException +#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 + /// Initializes a new instance of the class with serialized data /// - /// The serialization info - /// The streaming context + /// The object that holds the serialized data + /// The contextual information about the source or destination +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif private JsFatalException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/JsObjectFinalizeCallback.cs b/src/MsieJavaScriptEngine/JsRt/JsFinalizeCallback.cs similarity index 60% rename from src/MsieJavaScriptEngine/JsRt/JsObjectFinalizeCallback.cs rename to src/MsieJavaScriptEngine/JsRt/JsFinalizeCallback.cs index 0069182..df3d031 100644 --- a/src/MsieJavaScriptEngine/JsRt/JsObjectFinalizeCallback.cs +++ b/src/MsieJavaScriptEngine/JsRt/JsFinalizeCallback.cs @@ -1,10 +1,10 @@ -namespace MsieJavaScriptEngine.JsRt -{ - using System; +using System; +namespace MsieJavaScriptEngine.JsRt +{ /// /// The finalization callback /// /// The external data that was passed in when creating the object being finalized - internal delegate void JsObjectFinalizeCallback(IntPtr data); + internal delegate void JsFinalizeCallback(IntPtr data); } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/JsMemoryAllocationCallback.cs b/src/MsieJavaScriptEngine/JsRt/JsMemoryAllocationCallback.cs index 992a888..3e42539 100644 --- a/src/MsieJavaScriptEngine/JsRt/JsMemoryAllocationCallback.cs +++ b/src/MsieJavaScriptEngine/JsRt/JsMemoryAllocationCallback.cs @@ -1,16 +1,16 @@ -namespace MsieJavaScriptEngine.JsRt -{ - using System; +using System; +namespace MsieJavaScriptEngine.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); diff --git a/src/MsieJavaScriptEngine/JsRt/JsRuntimeAttributes.cs b/src/MsieJavaScriptEngine/JsRt/JsRuntimeAttributes.cs index a4f8706..a4c3536 100644 --- a/src/MsieJavaScriptEngine/JsRt/JsRuntimeAttributes.cs +++ b/src/MsieJavaScriptEngine/JsRt/JsRuntimeAttributes.cs @@ -1,8 +1,8 @@ -namespace MsieJavaScriptEngine.JsRt -{ - using System; - using System.Diagnostics.CodeAnalysis; +using System; +using System.Diagnostics.CodeAnalysis; +namespace MsieJavaScriptEngine.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,9 +38,9 @@ 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, + DisableEval = 0x00000010 } } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/JsSourceContext.cs b/src/MsieJavaScriptEngine/JsRt/JsSourceContext.cs index 7f2780b..12584f2 100644 --- a/src/MsieJavaScriptEngine/JsRt/JsSourceContext.cs +++ b/src/MsieJavaScriptEngine/JsRt/JsSourceContext.cs @@ -1,7 +1,7 @@ -namespace MsieJavaScriptEngine.JsRt -{ - using System; +using System; +namespace MsieJavaScriptEngine.JsRt +{ /// /// The cookie that identifies a script for debugging purposes /// diff --git a/src/MsieJavaScriptEngine/JsRt/JsThreadServiceCallback.cs b/src/MsieJavaScriptEngine/JsRt/JsThreadServiceCallback.cs index 9008444..5ab1e0d 100644 --- a/src/MsieJavaScriptEngine/JsRt/JsThreadServiceCallback.cs +++ b/src/MsieJavaScriptEngine/JsRt/JsThreadServiceCallback.cs @@ -1,7 +1,7 @@ -namespace MsieJavaScriptEngine.JsRt -{ - using System; +using System; +namespace MsieJavaScriptEngine.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/MsieJavaScriptEngine/JsRt/JsUsageException.cs b/src/MsieJavaScriptEngine/JsRt/JsUsageException.cs index 1682791..55ba243 100644 --- a/src/MsieJavaScriptEngine/JsRt/JsUsageException.cs +++ b/src/MsieJavaScriptEngine/JsRt/JsUsageException.cs @@ -1,38 +1,48 @@ -namespace MsieJavaScriptEngine.JsRt -{ - using System; - using System.Runtime.Serialization; +#if !NETSTANDARD1_3 +using System; +using System.Runtime.Serialization; +#endif +namespace MsieJavaScriptEngine.JsRt +{ /// - /// The API usage exception + /// The API usage exception occurred /// +#if !NETSTANDARD1_3 [Serializable] - internal sealed class JsUsageException : JsException +#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 + /// Initializes a new instance of the class with serialized data /// - /// The serialization info - /// The streaming context + /// The object that holds the serialized data + /// The contextual information about the source or destination +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif private JsUsageException(SerializationInfo info, StreamingContext context) : base(info, context) { } +#endif } } \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/JsValueType.cs b/src/MsieJavaScriptEngine/JsRt/JsValueType.cs index b50cfa7..e51dcff 100644 --- a/src/MsieJavaScriptEngine/JsRt/JsValueType.cs +++ b/src/MsieJavaScriptEngine/JsRt/JsValueType.cs @@ -16,37 +16,37 @@ 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 } diff --git a/src/MsieJavaScriptEngine/JsRt/ProfilerEventMask.cs b/src/MsieJavaScriptEngine/JsRt/ProfilerEventMask.cs deleted file mode 100644 index 1715075..0000000 --- a/src/MsieJavaScriptEngine/JsRt/ProfilerEventMask.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace MsieJavaScriptEngine.JsRt -{ - /// - /// Event mask for profiling - /// - internal enum ProfilerEventMask - { - /// - /// Trace calls to script functions - /// - TraceScriptFunctionCall = 0x1, - - /// - /// Trace calls to built-in functions - /// - TraceNativeFunctionCall = 0x2, - - /// - /// Trace calls to DOM methods - /// - TraceDomFunctionCall = 0x4, - - /// - /// Trace all calls except DOM methods - /// - TraceAll = (TraceScriptFunctionCall | TraceNativeFunctionCall), - - /// - /// Trace all calls - /// - TraceAllWithDom = (TraceAll | TraceDomFunctionCall) - } -} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/ProfilerScriptType.cs b/src/MsieJavaScriptEngine/JsRt/ProfilerScriptType.cs deleted file mode 100644 index 212a51e..0000000 --- a/src/MsieJavaScriptEngine/JsRt/ProfilerScriptType.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace MsieJavaScriptEngine.JsRt -{ - /// - /// Profiled script type - /// - internal enum ProfilerScriptType - { - /// - /// The user script - /// - User, - - /// - /// The dynamic script - /// - Dynamic, - - /// - /// The native script - /// - Native, - - /// - /// The DOM-related script - /// - Dom - } -} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/JsRt/TypeMapper.cs b/src/MsieJavaScriptEngine/JsRt/TypeMapper.cs new file mode 100644 index 0000000..9c2b530 --- /dev/null +++ b/src/MsieJavaScriptEngine/JsRt/TypeMapper.cs @@ -0,0 +1,376 @@ +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; + +using MsieJavaScriptEngine.Helpers; +using MsieJavaScriptEngine.JsRt.Embedding; +using MsieJavaScriptEngine.Utilities; + +namespace MsieJavaScriptEngine.JsRt +{ + /// + /// Type mapper + /// + /// The type of the JavaScript value + /// The type of the native function + internal abstract class TypeMapper : IDisposable + where TValue : struct + where TFunction : Delegate + { + /// + /// Name of property to store the external object + /// + protected const string ExternalObjectPropertyName = "_MsieJavaScriptEngine_externalObject"; + + /// + /// Flag for whether to allow the usage of reflection API in the script code + /// + protected readonly bool _allowReflection; + + /// + /// Storage for lazy-initialized embedded objects + /// + private ConcurrentDictionary>> _lazyEmbeddedObjects; + + /// + /// Callback for finalization of embedded object + /// + protected JsFinalizeCallback _embeddedObjectFinalizeCallback; + + /// + /// Synchronizer of embedded object storage's initialization + /// + private readonly Lock _embeddedObjectStorageInitializationSynchronizer = new Lock(); + + /// + /// 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 + /// + protected JsFinalizeCallback _embeddedTypeFinalizeCallback; + + /// + /// Synchronizer of embedded type storage's initialization + /// + private readonly Lock _embeddedTypeStorageInitializationSynchronizer = new Lock(); + + /// + /// 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 the type mapper + /// + /// Flag for whether to allow the usage of reflection API in the script code + protected 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 virtual TValue 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 virtual TValue 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 abstract TValue MapToScriptType(object value); + + /// + /// Makes a mapping of value from the script type to a host type + /// + /// The source value + /// The mapped value + public abstract object MapToHostType(TValue value); + + protected abstract EmbeddedObject CreateEmbeddedObjectOrFunction(object obj); + + private void EmbeddedObjectFinalizeCallback(IntPtr ptr) + { + if (ptr == IntPtr.Zero) + { + return; + } + + GCHandle objHandle = GCHandle.FromIntPtr(ptr); + object obj = objHandle.Target; + var lazyEmbeddedObjects = _lazyEmbeddedObjects; + + if (obj is not null && lazyEmbeddedObjects is not null) + { + var embeddedObjectKey = new EmbeddedObjectKey(obj); + Lazy> lazyEmbeddedObject; + + if (lazyEmbeddedObjects.TryRemove(embeddedObjectKey, out lazyEmbeddedObject)) + { + lazyEmbeddedObject.Value?.Dispose(); + } + } + + objHandle.Free(); + } + + protected abstract EmbeddedType CreateEmbeddedType(Type type); + + 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 is not null) + { + Lazy> lazyEmbeddedType; + + if (lazyEmbeddedTypes.TryRemove(embeddedTypeKey, out lazyEmbeddedType)) + { + lazyEmbeddedType.Value?.Dispose(); + } + } + + embeddedTypeHandle.Free(); + } + + protected bool IsAvailableProperty(PropertyInfo property) + { + if (_allowReflection) + { + return true; + } + + bool isAvailable = ReflectionHelpers.IsAllowedProperty(property); + + return isAvailable; + } + + protected Dictionary> GetAvailableMethodGroups(MethodInfo[] methods) + { + int methodCount = methods.Length; + if (methodCount == 0) + { + return []; + } + + 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 = [method]; + availableMethodGroups.Add(methodName, methodGroup); + } + } + + return availableMethodGroups; + } + + protected object[] GetHostItemMemberArguments(TValue[] args, int maxArgCount = -1) + { + if (args is 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 = []; + } + + return processedArgs; + } + + protected static Exception UnwrapException(Exception exception) + { + Exception originalException = exception; + var targetInvocationException = exception as TargetInvocationException; + + if (targetInvocationException is not null) + { + Exception innerException = targetInvocationException.InnerException; + if (innerException is not null) + { + originalException = innerException; + } + } + + return originalException; + } + + #region IDisposable implementation + + /// + /// Disposes a type mapper + /// + public virtual void Dispose() + { + if (_disposedFlag.Set()) + { + var lazyEmbeddedObjects = _lazyEmbeddedObjects; + if (lazyEmbeddedObjects is not 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 is not 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/MsieJavaScriptEngine/JsRuntimeException.cs b/src/MsieJavaScriptEngine/JsRuntimeException.cs index 0357c90..7a30993 100644 --- a/src/MsieJavaScriptEngine/JsRuntimeException.cs +++ b/src/MsieJavaScriptEngine/JsRuntimeException.cs @@ -1,55 +1,35 @@ -namespace MsieJavaScriptEngine -{ - using System; +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#if !NET10_0_OR_GREATER +using System.Security.Permissions; +#endif +#endif + +using MsieJavaScriptEngine.Constants; +namespace MsieJavaScriptEngine +{ /// - /// 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 - /// - public int LineNumber - { - get; - set; - } - - /// - /// Gets or sets a column number + /// String representation of the script call stack /// - public int ColumnNumber - { - get; - set; - } + private string _callStack = string.Empty; /// - /// Gets or sets a source fragment + /// Gets or sets a string representation of the script call stack /// - public string SourceFragment + public string CallStack { - get; - set; + get { return _callStack; } + set { _callStack = value; } } @@ -59,44 +39,90 @@ public string SourceFragment /// /// The message that describes the error public JsRuntimeException(string message) - : this(message, string.Empty) - { } + : 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, 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 mode + /// Name of JS engine mode public JsRuntimeException(string message, string engineMode) - : this(message, engineMode, null) - { } + : base(message, engineMode) + { + 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 mode + /// Name of JS engine mode /// The exception that is the cause of the current exception public JsRuntimeException(string message, string engineMode, Exception innerException) : base(message, engineMode, 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 +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif + protected JsRuntimeException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + if (info is not 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 +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#else + [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] +#endif + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info is 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/MsieJavaScriptEngine/JsScriptException.cs b/src/MsieJavaScriptEngine/JsScriptException.cs new file mode 100644 index 0000000..87f83d0 --- /dev/null +++ b/src/MsieJavaScriptEngine/JsScriptException.cs @@ -0,0 +1,182 @@ +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#if !NET10_0_OR_GREATER +using System.Security.Permissions; +#endif +#endif + +namespace MsieJavaScriptEngine +{ + /// + /// 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 mode + public JsScriptException(string message, string engineMode) + : base(message, engineMode) + { } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine mode + /// The exception that is the cause of the current exception + public JsScriptException(string message, string engineMode, Exception innerException) + : base(message, engineMode, 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 +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif + protected JsScriptException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + if (info is not 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 +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#else + [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] +#endif + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info is 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/MsieJavaScriptEngine/JsUsageException.cs b/src/MsieJavaScriptEngine/JsUsageException.cs new file mode 100644 index 0000000..a3049e8 --- /dev/null +++ b/src/MsieJavaScriptEngine/JsUsageException.cs @@ -0,0 +1,79 @@ +using System; +#if !NETSTANDARD1_3 +using System.Runtime.Serialization; +#endif + +using MsieJavaScriptEngine.Constants; + +namespace MsieJavaScriptEngine +{ + /// + /// 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 mode + public JsUsageException(string message, string engineMode) + : base(message, engineMode) + { + Category = JsErrorCategory.Usage; + } + + /// + /// Initializes a new instance of the class + /// + /// The error message that explains the reason for the exception + /// Name of JS engine mode + /// The exception that is the cause of the current exception + public JsUsageException(string message, string engineMode, Exception innerException) + : base(message, engineMode, 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 +#if NET10_0_OR_GREATER + [Obsolete(DiagnosticId = "SYSLIB0051")] +#endif + private JsUsageException(SerializationInfo info, StreamingContext context) + : base(info, context) + { } +#endif + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/MsieJavaScriptEngine.csproj b/src/MsieJavaScriptEngine/MsieJavaScriptEngine.csproj index 0abd5f1..056da0a 100644 --- a/src/MsieJavaScriptEngine/MsieJavaScriptEngine.csproj +++ b/src/MsieJavaScriptEngine/MsieJavaScriptEngine.csproj @@ -1,196 +1,106 @@ - - - + + - Debug - AnyCPU - {D672BC49-C454-4975-BD25-A555B9BDD793} + MSIE JavaScript Engine for .NET + 3.3.1 + net40-client;net45;netstandard1.3;netstandard2.0;netstandard2.1;net10.0 + 1.6.0 Library - Properties - MsieJavaScriptEngine - MsieJavaScriptEngine - v4.0 - 512 - Client - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - true - - - ..\..\MsieJavaScriptEngine.snk + true + $(NoWarn);CS1591;NETSDK1215;NU1605;NU1903 + true + true + $(Product) + Andrey Taritsyn + This library is a .NET wrapper for working with the JavaScript engines of Internet Explorer and Edge Legacy. + README.md + Apache-2.0 + https://github.com/Taritsyn/MsieJavaScriptEngine + https://raw.githubusercontent.com/Taritsyn/MsieJavaScriptEngine/master/images/MsieJavaScriptEngine_Logo_128x128.png + icon.png + https://github.com/Taritsyn/MsieJavaScriptEngine + git + true + true + snupkg + JavaScript;ECMAScript;MSIE;IE;Edge;Chakra + Was made refactoring. + en-US + ../../nuget + true + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - True - True - Strings.resx - - - True - True - Strings.ru-ru.resx - - - - - - + + - - - + + + - - - - - + + + - - - ResXFileCodeGenerator - Strings.Designer.cs - Designer - - - ResXFileCodeGenerator - Strings.ru-ru.Designer.cs - Designer - + + + + + + + + + + + + + + + + + + + - - MsieJavaScriptEngine.snk + + $(PackageIcon) + true + false + + + / + true + false + + + / + true + false - - - - cd "$(ProjectDir)..\..\Binaries\Microsoft Ajax Minifier\" -del "$(ProjectDir)Resources\ES5.min.js" -AjaxMinifier.exe "$(ProjectDir)Resources\ES5.js" –out "$(ProjectDir)Resources\ES5.min.js" + + $(PackageReadmeFile) + true + + + / + true + + -del "$(ProjectDir)Resources\json2.min.js" -AjaxMinifier.exe "$(ProjectDir)Resources\json2.js" –out "$(ProjectDir)Resources\json2.min.js" - - + \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/MsieJsEngine.cs b/src/MsieJavaScriptEngine/MsieJsEngine.cs index 04c4766..1a62abf 100644 --- a/src/MsieJavaScriptEngine/MsieJsEngine.cs +++ b/src/MsieJavaScriptEngine/MsieJsEngine.cs @@ -1,35 +1,44 @@ -namespace MsieJavaScriptEngine +using System; +using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; + +#if NETFRAMEWORK +using MsieJavaScriptEngine.ActiveScript; +#endif +using MsieJavaScriptEngine.Helpers; +using MsieJavaScriptEngine.JsRt.Edge; +using MsieJavaScriptEngine.JsRt.Ie; +using MsieJavaScriptEngine.Resources; +using MsieJavaScriptEngine.Utilities; + +namespace MsieJavaScriptEngine { - using System; - using System.Reflection; - using System.Text; - - using ActiveScript; - using Helpers; - using JsRt.Edge; - using JsRt.Ie; - using Resources; - using Utilities; - /// - /// .NET-wrapper for working with the Internet Explorer's JavaScript engines + /// .NET-wrapper for working with the Internet Explorer's JS engines /// public sealed class MsieJsEngine : IDisposable { /// - /// JavaScript engine + /// JS engine /// private IInnerJsEngine _jsEngine; /// - /// Current JavaScript engine mode + /// Current JS engine mode /// private static JsEngineMode _currentMode; /// - /// Synchronizer of JavaScript engines creation + /// Synchronizer of JS engines creation + /// + private static readonly Lock _creationSynchronizer = new Lock(); + + /// + /// Unique document name manager /// - private static readonly object _creationSynchronizer = new object(); + private UniqueDocumentNameManager _documentNameManager = new UniqueDocumentNameManager("Script Document"); /// /// Flag that object is destroyed @@ -37,33 +46,42 @@ public sealed class MsieJsEngine : IDisposable private InterlockedStatedFlag _disposedFlag = new InterlockedStatedFlag(); /// - /// Gets a name of JavaScript engine mode + /// Gets a name of JS engine mode /// public string Mode { get { return _jsEngine.Mode; } } + /// + /// Gets a value that indicates if the JS engine supports script pre-compilation + /// + public bool SupportsScriptPrecompilation + { + get { return _jsEngine.SupportsScriptPrecompilation; } + } + /// - /// Constructs instance of MSIE JavaScript engine + /// Constructs an instance of MSIE JS engine /// - /// Failed to load a JavaScript engine. - /// Selected mode of JavaScript engine is not supported. + /// + /// public MsieJsEngine() : this(new JsEngineSettings()) { } /// - /// Constructs instance of MSIE JavaScript engine + /// Constructs an instance of MSIE JS engine /// - /// JavaScript engine settings - /// Failed to load a JavaScript engine. - /// Selected mode of JavaScript engine is not supported. + /// JS engine settings + /// + /// public MsieJsEngine(JsEngineSettings settings) { JsEngineMode engineMode = settings.EngineMode; JsEngineMode processedEngineMode = engineMode; + JsEngineSettings processedSettings = settings; if (engineMode == JsEngineMode.Auto) { @@ -75,6 +93,7 @@ public MsieJsEngine(JsEngineSettings settings) { processedEngineMode = JsEngineMode.ChakraIeJsRt; } +#if NETFRAMEWORK else if (ChakraActiveScriptJsEngine.IsSupported()) { processedEngineMode = JsEngineMode.ChakraActiveScript; @@ -83,12 +102,25 @@ public MsieJsEngine(JsEngineSettings settings) { processedEngineMode = JsEngineMode.Classic; } +#endif else { - throw new JsEngineLoadException(Strings.Runtime_JsEnginesNotFound); + throw new JsEngineLoadException( +#if !NETFRAMEWORK + NetCoreStrings.Engine_JsEnginesNotFound +#else + NetFrameworkStrings.Engine_JsEnginesNotFound +#endif + ); } } + if (processedEngineMode != engineMode) + { + processedSettings = settings.Clone(); + processedSettings.EngineMode = processedEngineMode; + } + lock (_creationSynchronizer) { JsEngineMode previousMode = _currentMode; @@ -99,13 +131,13 @@ public MsieJsEngine(JsEngineSettings settings) if (previousMode != JsEngineMode.ChakraIeJsRt && previousMode != JsEngineMode.ChakraActiveScript) { - _jsEngine = new ChakraEdgeJsRtJsEngine(settings.EnableDebugging); + _jsEngine = new ChakraEdgeJsRtJsEngine(processedSettings); } else if (previousMode == JsEngineMode.ChakraIeJsRt) { - throw new JsEngineLoadException( + throw new JsUsageException( string.Format( - Strings.Runtime_JsEnginesConflictInProcess, + CommonStrings.Usage_JsEnginesConflictInProcess, JsEngineModeHelpers.GetModeName(processedEngineMode), JsEngineModeHelpers.GetModeName(previousMode) ) @@ -113,9 +145,9 @@ public MsieJsEngine(JsEngineSettings settings) } else if (previousMode == JsEngineMode.ChakraActiveScript) { - throw new JsEngineLoadException( + throw new JsUsageException( string.Format( - Strings.Runtime_JsEnginesConflictInProcess, + CommonStrings.Usage_JsEnginesConflictInProcess, JsEngineModeHelpers.GetModeName(processedEngineMode), JsEngineModeHelpers.GetModeName(previousMode) ) @@ -126,13 +158,13 @@ public MsieJsEngine(JsEngineSettings settings) case JsEngineMode.ChakraIeJsRt: if (previousMode != JsEngineMode.ChakraEdgeJsRt) { - _jsEngine = new ChakraIeJsRtJsEngine(settings.EnableDebugging); + _jsEngine = new ChakraIeJsRtJsEngine(processedSettings); } else { - throw new JsEngineLoadException( + throw new JsUsageException( string.Format( - Strings.Runtime_JsEnginesConflictInProcess, + CommonStrings.Usage_JsEnginesConflictInProcess, JsEngineModeHelpers.GetModeName(processedEngineMode), JsEngineModeHelpers.GetModeName(previousMode) ) @@ -141,15 +173,17 @@ public MsieJsEngine(JsEngineSettings settings) break; case JsEngineMode.ChakraActiveScript: +#if NETFRAMEWORK if (previousMode != JsEngineMode.ChakraEdgeJsRt) { - _jsEngine = new ChakraActiveScriptJsEngine(); + + _jsEngine = new ChakraActiveScriptJsEngine(processedSettings); } else { - throw new JsEngineLoadException( + throw new JsUsageException( string.Format( - Strings.Runtime_JsEnginesConflictInProcess, + CommonStrings.Usage_JsEnginesConflictInProcess, JsEngineModeHelpers.GetModeName(processedEngineMode), JsEngineModeHelpers.GetModeName(previousMode) ) @@ -157,13 +191,22 @@ public MsieJsEngine(JsEngineSettings settings) } break; +#else + throw new JsUsageException( + string.Format(NetCoreStrings.Usage_JsEngineModeNotCompatibleWithNetCore, processedEngineMode)); +#endif case JsEngineMode.Classic: - _jsEngine = new ClassicActiveScriptJsEngine(settings.UseEcmaScript5Polyfill, - settings.UseJson2Library); +#if NETFRAMEWORK + _jsEngine = new ClassicActiveScriptJsEngine(processedSettings); + break; +#else + throw new JsUsageException( + string.Format(NetCoreStrings.Usage_JsEngineModeNotCompatibleWithNetCore, processedEngineMode)); +#endif default: - throw new NotSupportedException( - string.Format(Strings.Runtime_JsEngineModeNotSupported, processedEngineMode)); + throw new JsUsageException( + string.Format(CommonStrings.Usage_JsEngineModeNotSupported, processedEngineMode)); } _currentMode = processedEngineMode; @@ -171,6 +214,7 @@ public MsieJsEngine(JsEngineSettings settings) } + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] private void VerifyNotDisposed() { if (_disposedFlag.IsSet()) @@ -179,58 +223,389 @@ private void VerifyNotDisposed() } } + /// + /// Creates a pre-compiled script from JS code + /// + /// JS code + /// A pre-compiled script that can be executed by different instances of JS engine + /// + /// + /// + /// + /// + public PrecompiledScript Precompile(string code) + { + return Precompile(code, string.Empty); + } + + /// + /// 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 + /// + /// + /// + /// + /// + public PrecompiledScript Precompile(string code, string documentName) + { + VerifyNotDisposed(); + + if (code is null) + { + throw new ArgumentNullException( + nameof(code), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(code)) + ); + } + + if (string.IsNullOrWhiteSpace(code)) + { + throw new ArgumentException( + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(code)), + nameof(code) + ); + } + + if (!string.IsNullOrWhiteSpace(documentName) + && !ValidationHelpers.CheckDocumentNameFormat(documentName)) + { + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidDocumentNameFormat, documentName), + nameof(documentName) + ); + } + + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); + + return _jsEngine.Precompile(code, uniqueDocumentName); + } + + /// + /// 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 + /// + /// + /// + /// + /// + /// + /// + public PrecompiledScript PrecompileFile(string path, Encoding encoding = null) + { + VerifyNotDisposed(); + + if (path is null) + { + throw new ArgumentNullException( + nameof(path), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(path)) + ); + } + + if (string.IsNullOrWhiteSpace(path)) + { + throw new ArgumentException( + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(path)), + nameof(path) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(path)) + { + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidFileNameFormat, path), + nameof(path) + ); + } + + string code = Utils.GetFileTextContent(path, encoding); + if (string.IsNullOrWhiteSpace(code)) + { + throw new JsUsageException( + string.Format(CommonStrings.Usage_CannotPrecompileEmptyFile, path), + _jsEngine.Mode + ); + } + string uniqueDocumentName = _documentNameManager.GetUniqueName(path); + + return _jsEngine.Precompile(code, uniqueDocumentName); + } + + /// + /// 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 + /// + /// + /// + /// + /// + /// + /// + public PrecompiledScript PrecompileResource(string resourceName, Type type) + { + VerifyNotDisposed(); + + if (resourceName is null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (type is null) + { + throw new ArgumentNullException( + nameof(type), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(type)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(resourceName)) + { + throw new ArgumentException( + string.Format(CommonStrings.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 is not null ? nameSpace + "." + resourceName : resourceName; + + string code = Utils.GetResourceAsString(resourceFullName, assembly); + if (string.IsNullOrWhiteSpace(code)) + { + throw new JsUsageException( + string.Format(CommonStrings.Usage_CannotPrecompileEmptyResource, resourceFullName), + _jsEngine.Mode + ); + } + string uniqueDocumentName = _documentNameManager.GetUniqueName(resourceFullName); + + return _jsEngine.Precompile(code, uniqueDocumentName); + } + + /// + /// 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 + /// + /// + /// + /// + /// + /// + /// + public PrecompiledScript PrecompileResource(string resourceName, Assembly assembly) + { + VerifyNotDisposed(); + + if (resourceName is null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (assembly is null) + { + throw new ArgumentNullException( + nameof(assembly), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(assembly)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(resourceName)) + { + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidResourceNameFormat, resourceName), + nameof(resourceName) + ); + } + + string code = Utils.GetResourceAsString(resourceName, assembly); + if (string.IsNullOrWhiteSpace(code)) + { + throw new JsUsageException( + string.Format(CommonStrings.Usage_CannotPrecompileEmptyResource, resourceName), + _jsEngine.Mode + ); + } + string uniqueDocumentName = _documentNameManager.GetUniqueName(resourceName); + + return _jsEngine.Precompile(code, uniqueDocumentName); + } + /// /// Evaluates an expression /// - /// JavaScript expression + /// JS expression /// Result of the expression - /// Operation is performed on a disposed MSIE - /// JavaScript engine. - /// - /// JavaScript runtime error. + /// + /// + /// + /// + /// + /// + /// public object Evaluate(string expression) + { + return Evaluate(expression, string.Empty); + } + + /// + /// Evaluates an expression + /// + /// JS expression + /// Document name + /// Result of the expression + /// + /// + /// + /// + /// + /// + /// + public object Evaluate(string expression, string documentName) { VerifyNotDisposed(); + if (expression is null) + { + throw new ArgumentNullException( + nameof(expression), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(expression)) + ); + } + if (string.IsNullOrWhiteSpace(expression)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "expression"), "expression"); + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(expression)), + nameof(expression) + ); + } + + if (!string.IsNullOrWhiteSpace(documentName) + && !ValidationHelpers.CheckDocumentNameFormat(documentName)) + { + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidDocumentNameFormat, documentName), + nameof(documentName) + ); } - return _jsEngine.Evaluate(expression); + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); + + return _jsEngine.Evaluate(expression, uniqueDocumentName); } /// /// Evaluates an expression /// /// Type of result - /// JavaScript expression + /// JS expression /// Result of the expression - /// Operation is performed on a disposed MSIE - /// JavaScript engine. - /// - /// The type of return value - /// is not supported. - /// JavaScript runtime error. + /// + /// + /// + /// + /// + /// + /// public T Evaluate(string expression) + { + return Evaluate(expression, string.Empty); + } + + /// + /// Evaluates an expression + /// + /// Type of result + /// JS expression + /// Document name + /// Result of the expression + /// + /// + /// + /// + /// + /// + /// + public T Evaluate(string expression, string documentName) { VerifyNotDisposed(); + if (expression is null) + { + throw new ArgumentNullException( + nameof(expression), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(expression)) + ); + } + if (string.IsNullOrWhiteSpace(expression)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "expression"), "expression"); + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(expression)), + nameof(expression) + ); + } + + if (!string.IsNullOrWhiteSpace(documentName) + && !ValidationHelpers.CheckDocumentNameFormat(documentName)) + { + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidDocumentNameFormat, documentName), + nameof(documentName) + ); } Type returnValueType = typeof(T); if (!ValidationHelpers.IsSupportedType(returnValueType)) { - throw new NotSupportedTypeException( - string.Format(Strings.Runtime_ReturnValueTypeNotSupported, returnValueType.FullName)); + throw new ArgumentException( + string.Format(CommonStrings.Usage_ReturnValueTypeNotSupported, returnValueType.FullName), + nameof(T) + ); } - object result = _jsEngine.Evaluate(expression); + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); + object result = _jsEngine.Evaluate(expression, uniqueDocumentName); return TypeConverter.ConvertToType(result); } @@ -238,106 +613,288 @@ public T Evaluate(string expression) /// /// Executes a code /// - /// JavaScript code - /// Operation is performed on a disposed MSIE - /// JavaScript engine. - /// - /// JavaScript runtime error. + /// JS code + /// + /// + /// + /// + /// + /// + /// public void Execute(string code) + { + Execute(code, string.Empty); + } + + /// + /// Executes a code + /// + /// JS code + /// Document name + /// + /// + /// + /// + /// + /// + /// + public void Execute(string code, string documentName) { VerifyNotDisposed(); + if (code is null) + { + throw new ArgumentNullException( + nameof(code), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(code)) + ); + } + if (string.IsNullOrWhiteSpace(code)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "code"), "code"); + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(code)), + nameof(code) + ); + } + + if (!string.IsNullOrWhiteSpace(documentName) + && !ValidationHelpers.CheckDocumentNameFormat(documentName)) + { + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidDocumentNameFormat, documentName), + nameof(documentName) + ); + } + + string uniqueDocumentName = _documentNameManager.GetUniqueName(documentName); + _jsEngine.Execute(code, uniqueDocumentName); + } + + /// + /// Executes a pre-compiled script + /// + /// A pre-compiled script that can be executed by different + /// instances of JS engine + /// + /// + /// + /// + /// + /// + /// + public void Execute(PrecompiledScript precompiledScript) + { + VerifyNotDisposed(); + + if (precompiledScript is null) + { + throw new ArgumentNullException( + nameof(precompiledScript), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(precompiledScript)) + ); + } + + if (precompiledScript.EngineMode != Mode) + { + throw new JsUsageException( + string.Format(CommonStrings.Usage_CannotExecutePrecompiledScriptForAnotherJsEngineMode, + precompiledScript.EngineMode), + _jsEngine.Mode + ); } - _jsEngine.Execute(code); + _jsEngine.Execute(precompiledScript); } /// - /// Executes a code from JS-file + /// Executes a code from JS file /// - /// Path to the JS-file + /// Path to the JS file /// Text encoding - /// Operation is performed on a disposed MSIE - /// JavaScript engine. - /// - /// Specified JS-file not found. - /// JavaScript runtime error. + /// + /// + /// + /// + /// + /// + /// + /// + /// public void ExecuteFile(string path, Encoding encoding = null) { VerifyNotDisposed(); + if (path is null) + { + throw new ArgumentNullException( + nameof(path), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(path)) + ); + } + if (string.IsNullOrWhiteSpace(path)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "path"), "path"); + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(path)), + nameof(path) + ); + } + + if (!ValidationHelpers.CheckDocumentNameFormat(path)) + { + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidFileNameFormat, path), + nameof(path) + ); } string code = Utils.GetFileTextContent(path, encoding); - Execute(code); + if (string.IsNullOrWhiteSpace(code)) + { + throw new JsUsageException( + string.Format(CommonStrings.Usage_CannotExecuteEmptyFile, path), + _jsEngine.Mode + ); + } + string uniqueDocumentName = _documentNameManager.GetUniqueName(path); + + _jsEngine.Execute(code, uniqueDocumentName); } /// - /// 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 - /// Operation is performed on a disposed MSIE - /// JavaScript engine. - /// - /// - /// JavaScript runtime error. + /// 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 + /// + /// + /// + /// + /// + /// + /// + /// + /// public void ExecuteResource(string resourceName, Type type) { VerifyNotDisposed(); + if (resourceName is null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (type is null) + { + throw new ArgumentNullException( + nameof(type), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(type)) + ); + } + if (string.IsNullOrWhiteSpace(resourceName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "resourceName"), "resourceName"); + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); } - if (type == null) + if (!ValidationHelpers.CheckDocumentNameFormat(resourceName)) { - throw new ArgumentNullException( - "type", string.Format(Strings.Common_ArgumentIsNull, "type")); + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidResourceNameFormat, resourceName), + nameof(resourceName) + ); } - string code = Utils.GetResourceAsString(resourceName, type); - Execute(code); +#if NET40 + Assembly assembly = type.Assembly; +#else + Assembly assembly = type.GetTypeInfo().Assembly; +#endif + string nameSpace = type.Namespace; + string resourceFullName = nameSpace is not null ? nameSpace + "." + resourceName : resourceName; + + string code = Utils.GetResourceAsString(resourceFullName, assembly); + if (string.IsNullOrWhiteSpace(code)) + { + throw new JsUsageException( + string.Format(CommonStrings.Usage_CannotExecuteEmptyResource, resourceFullName), + _jsEngine.Mode + ); + } + string uniqueDocumentName = _documentNameManager.GetUniqueName(resourceFullName); + + _jsEngine.Execute(code, uniqueDocumentName); } /// - /// Executes a code from embedded JS-resource + /// Executes a code from embedded JS resource /// - /// JS-resource name - /// Assembly that containing an embedded resource - /// Operation is performed on a disposed MSIE - /// JavaScript engine. - /// - /// - /// JavaScript runtime error. + /// The case-sensitive resource name + /// The assembly, which contains the embedded resource + /// + /// + /// + /// + /// + /// + /// + /// + /// public void ExecuteResource(string resourceName, Assembly assembly) { VerifyNotDisposed(); + if (resourceName is null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (assembly is null) + { + throw new ArgumentNullException( + nameof(assembly), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(assembly)) + ); + } + if (string.IsNullOrWhiteSpace(resourceName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "resourceName"), "resourceName"); + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); } - if (assembly == null) + if (!ValidationHelpers.CheckDocumentNameFormat(resourceName)) { - throw new ArgumentNullException( - "assembly", string.Format(Strings.Common_ArgumentIsNull, "assembly")); + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidResourceNameFormat, resourceName), + nameof(resourceName) + ); } string code = Utils.GetResourceAsString(resourceName, assembly); - Execute(code); + if (string.IsNullOrWhiteSpace(code)) + { + throw new JsUsageException( + string.Format(CommonStrings.Usage_CannotExecuteEmptyResource, resourceName), + _jsEngine.Mode + ); + } + string uniqueDocumentName = _documentNameManager.GetUniqueName(resourceName); + + _jsEngine.Execute(code, uniqueDocumentName); } /// @@ -346,27 +903,38 @@ public void ExecuteResource(string resourceName, Assembly assembly) /// Function name /// Function arguments /// Result of the function execution - /// Operation is performed on a disposed MSIE - /// JavaScript engine. - /// - /// The function name has incorrect format. - /// The type of one function - /// parameter is not supported. - /// JavaScript runtime error. + /// + /// + /// + /// + /// + /// public object CallFunction(string functionName, params object[] args) { VerifyNotDisposed(); + if (functionName is null) + { + throw new ArgumentNullException( + nameof(functionName), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(functionName)) + ); + } + if (string.IsNullOrWhiteSpace(functionName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "functionName"), "functionName"); + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(functionName)), + nameof(functionName) + ); } if (!ValidationHelpers.CheckNameFormat(functionName)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidFunctionNameFormat, functionName)); + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidFunctionNameFormat, functionName), + nameof(functionName) + ); } int argumentCount = args.Length; @@ -376,15 +944,17 @@ public object CallFunction(string functionName, params object[] args) { object argument = args[argumentIndex]; - if (argument != null) + if (argument is not null) { Type argType = argument.GetType(); if (!ValidationHelpers.IsSupportedType(argType)) { - throw new NotSupportedTypeException( - string.Format(Strings.Runtime_FunctionParameterTypeNotSupported, - functionName, argType.FullName)); + throw new ArgumentException( + string.Format(CommonStrings.Usage_FunctionParameterTypeNotSupported, + functionName, argType.FullName), + nameof(args) + ); } } } @@ -402,34 +972,38 @@ public object CallFunction(string functionName, params object[] args) /// Function name /// Function arguments /// Result of the function execution - /// Operation is performed on a disposed MSIE - /// JavaScript engine. - /// - /// The function name has incorrect format. - /// The type of return value or - /// one function parameter is not supported. - /// JavaScript runtime error. + /// + /// + /// + /// + /// + /// public T CallFunction(string functionName, params object[] args) { VerifyNotDisposed(); - if (string.IsNullOrWhiteSpace(functionName)) + if (functionName is null) { - throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "functionName"), "functionName"); + throw new ArgumentNullException( + nameof(functionName), + string.Format(CommonStrings.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(CommonStrings.Common_ArgumentIsEmpty, nameof(functionName)), + nameof(functionName) + ); } if (!ValidationHelpers.CheckNameFormat(functionName)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidFunctionNameFormat, functionName)); + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidFunctionNameFormat, functionName), + nameof(functionName) + ); } int argumentCount = args.Length; @@ -439,49 +1013,72 @@ public T CallFunction(string functionName, params object[] args) { object argument = args[argumentIndex]; - if (argument != null) + if (argument is not null) { Type argType = argument.GetType(); if (!ValidationHelpers.IsSupportedType(argType)) { - throw new NotSupportedTypeException( - string.Format(Strings.Runtime_FunctionParameterTypeNotSupported, - functionName, argType.FullName)); + throw new ArgumentException( + string.Format(CommonStrings.Usage_FunctionParameterTypeNotSupported, + functionName, argType.FullName), + nameof(args) + ); } } } } + Type returnValueType = typeof(T); + if (!ValidationHelpers.IsSupportedType(returnValueType)) + { + throw new ArgumentException( + string.Format(CommonStrings.Usage_ReturnValueTypeNotSupported, returnValueType.FullName), + nameof(T) + ); + } + object result = _jsEngine.CallFunction(functionName, args); return TypeConverter.ConvertToType(result); } /// - /// Сhecks for the existence of a variable + /// Checks for the existence of a variable /// /// Name of variable - /// Result of check (true - exists; false - not exists - /// Operation is performed on a disposed MSIE - /// JavaScript engine. - /// - /// The variable name has incorrect format. - /// JavaScript runtime error. + /// Result of check (true - exists; false - not exists + /// + /// + /// + /// + /// public bool HasVariable(string variableName) { VerifyNotDisposed(); + if (variableName is null) + { + throw new ArgumentNullException( + nameof(variableName), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(variableName)) + ); + } + if (string.IsNullOrWhiteSpace(variableName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "variableName"), "variableName"); + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(variableName)), + nameof(variableName) + ); } if (!ValidationHelpers.CheckNameFormat(variableName)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidVariableNameFormat, variableName)); + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidVariableNameFormat, variableName), + nameof(variableName) + ); } return _jsEngine.HasVariable(variableName); @@ -492,25 +1089,37 @@ public bool HasVariable(string variableName) /// /// Name of variable /// Value of variable - /// Operation is performed on a disposed MSIE - /// JavaScript engine. - /// - /// The variable name has incorrect format. - /// JavaScript runtime error. + /// + /// + /// + /// + /// public object GetVariableValue(string variableName) { VerifyNotDisposed(); + if (variableName is null) + { + throw new ArgumentNullException( + nameof(variableName), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(variableName)) + ); + } + if (string.IsNullOrWhiteSpace(variableName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "variableName"), "variableName"); + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(variableName)), + nameof(variableName) + ); } if (!ValidationHelpers.CheckNameFormat(variableName)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidVariableNameFormat, variableName)); + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidVariableNameFormat, variableName), + nameof(variableName) + ); } return _jsEngine.GetVariableValue(variableName); @@ -522,34 +1131,46 @@ public object GetVariableValue(string variableName) /// Type of variable /// Name of variable /// Value of variable - /// Operation is performed on a disposed MSIE - /// JavaScript engine. - /// - /// The variable name has incorrect format. - /// The type of return value - /// is not supported. - /// JavaScript runtime error. + /// + /// + /// + /// + /// public T GetVariableValue(string variableName) { VerifyNotDisposed(); + if (variableName is null) + { + throw new ArgumentNullException( + nameof(variableName), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(variableName)) + ); + } + if (string.IsNullOrWhiteSpace(variableName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "variableName"), "variableName"); + string.Format(CommonStrings.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(CommonStrings.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(CommonStrings.Usage_ReturnValueTypeNotSupported, returnValueType.FullName), + nameof(T) + ); } object result = _jsEngine.GetVariableValue(variableName); @@ -562,38 +1183,50 @@ public T GetVariableValue(string variableName) /// /// Name of variable /// Value of variable - /// Operation is performed on a disposed MSIE - /// JavaScript engine. - /// - /// The variable name has incorrect format. - /// The type of variable value - /// is not supported. - /// JavaScript runtime error. + /// + /// + /// + /// + /// public void SetVariableValue(string variableName, object value) { VerifyNotDisposed(); + if (variableName is null) + { + throw new ArgumentNullException( + nameof(variableName), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(variableName)) + ); + } + if (string.IsNullOrWhiteSpace(variableName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "variableName"), "variableName"); + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(variableName)), + nameof(variableName) + ); } if (!ValidationHelpers.CheckNameFormat(variableName)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidVariableNameFormat, variableName)); + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidVariableNameFormat, variableName), + nameof(variableName) + ); } - if (value != null) + if (value is not null) { Type variableType = value.GetType(); if (!ValidationHelpers.IsSupportedType(variableType)) { - throw new NotSupportedTypeException( - string.Format(Strings.Runtime_VariableTypeNotSupported, - variableName, variableType.FullName)); + throw new ArgumentException( + string.Format(CommonStrings.Usage_VariableTypeNotSupported, + variableName, variableType.FullName), + nameof(value) + ); } } @@ -604,25 +1237,37 @@ public void SetVariableValue(string variableName, object value) /// Removes a variable /// /// Name of variable - /// Operation is performed on a disposed MSIE - /// JavaScript engine. - /// - /// The variable name has incorrect format. - /// JavaScript runtime error. + /// + /// + /// + /// + /// public void RemoveVariable(string variableName) { VerifyNotDisposed(); + if (variableName is null) + { + throw new ArgumentNullException( + nameof(variableName), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(variableName)) + ); + } + if (string.IsNullOrWhiteSpace(variableName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "variableName"), "variableName"); + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(variableName)), + nameof(variableName) + ); } if (!ValidationHelpers.CheckNameFormat(variableName)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidVariableNameFormat, variableName)); + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidVariableNameFormat, variableName), + nameof(variableName) + ); } _jsEngine.RemoveVariable(variableName); @@ -631,39 +1276,60 @@ public 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. + /// + /// + /// + /// public void EmbedHostObject(string itemName, object value) { VerifyNotDisposed(); + if (itemName is null) + { + throw new ArgumentNullException( + nameof(itemName), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(itemName)) + ); + } + + if (value is null) + { + throw new ArgumentNullException( + nameof(value), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(value)) + ); + } + if (string.IsNullOrWhiteSpace(itemName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "itemName"), "itemName"); + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(itemName)), + nameof(itemName) + ); } if (!ValidationHelpers.CheckNameFormat(itemName)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidScriptItemNameFormat, itemName)); + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidScriptItemNameFormat, itemName), + nameof(itemName) + ); } - if (value != null) - { - Type itemType = value.GetType(); + Type itemType = value.GetType(); - if (ValidationHelpers.IsPrimitiveType(itemType) - || itemType == typeof (Undefined)) - { - throw new NotSupportedTypeException( - string.Format(Strings.Runtime_EmbeddedHostObjectTypeNotSupported, itemName, itemType.FullName)); - } - } - else + if (ValidationHelpers.IsPrimitiveType(itemType) || itemType == typeof(Undefined)) { - throw new ArgumentNullException("value", string.Format(Strings.Common_ArgumentIsNull, "value")); + throw new ArgumentException( + string.Format(CommonStrings.Usage_EmbeddedHostObjectTypeNotSupported, + itemName, itemType.FullName), + nameof(value) + ); } _jsEngine.EmbedHostObject(itemName, value); @@ -672,45 +1338,83 @@ public 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 + /// + /// + /// + /// public void EmbedHostType(string itemName, Type type) { VerifyNotDisposed(); + if (itemName is null) + { + throw new ArgumentNullException( + nameof(itemName), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(itemName)) + ); + } + + if (type is null) + { + throw new ArgumentNullException( + nameof(type), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(type)) + ); + } + if (string.IsNullOrWhiteSpace(itemName)) { throw new ArgumentException( - string.Format(Strings.Common_ArgumentIsEmpty, "itemName"), "itemName"); + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(itemName)), + nameof(itemName) + ); } if (!ValidationHelpers.CheckNameFormat(itemName)) { - throw new FormatException( - string.Format(Strings.Runtime_InvalidScriptItemNameFormat, itemName)); + throw new ArgumentException( + string.Format(CommonStrings.Usage_InvalidScriptItemNameFormat, itemName), + nameof(itemName) + ); } - if (type != null) + if (ValidationHelpers.IsPrimitiveType(type) || type == typeof(Undefined)) { - if (ValidationHelpers.IsPrimitiveType(type) - || type == typeof(Undefined)) - { - throw new NotSupportedTypeException( - string.Format(Strings.Runtime_EmbeddedHostTypeNotSupported, type.FullName)); - } - } - else - { - throw new ArgumentNullException("type", string.Format(Strings.Common_ArgumentIsNull, "type")); + throw new ArgumentException( + string.Format(CommonStrings.Usage_EmbeddedHostTypeNotSupported, type.FullName), + nameof(type) + ); } _jsEngine.EmbedHostType(itemName, type); } + /// + /// Interrupts script execution and causes the JS engine to throw an exception + /// + public void Interrupt() + { + VerifyNotDisposed(); + + _jsEngine.Interrupt(); + } + + /// + /// Performs a full garbage collection + /// + public void CollectGarbage() + { + VerifyNotDisposed(); + + _jsEngine.CollectGarbage(); + } + #region IDisposable implementation /// @@ -720,11 +1424,13 @@ public void Dispose() { if (_disposedFlag.Set()) { - if (_jsEngine != null) + if (_jsEngine is not null) { _jsEngine.Dispose(); _jsEngine = null; } + + _documentNameManager = null; } } diff --git a/src/MsieJavaScriptEngine/NotSupportedTypeException.cs b/src/MsieJavaScriptEngine/NotSupportedTypeException.cs deleted file mode 100644 index e57b1ca..0000000 --- a/src/MsieJavaScriptEngine/NotSupportedTypeException.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace MsieJavaScriptEngine -{ - 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/MsieJavaScriptEngine/PACKAGE-DESCRIPTION.md b/src/MsieJavaScriptEngine/PACKAGE-DESCRIPTION.md new file mode 100644 index 0000000..85c08b4 --- /dev/null +++ b/src/MsieJavaScriptEngine/PACKAGE-DESCRIPTION.md @@ -0,0 +1,2 @@ +This library is a .NET wrapper for working with the JavaScript engines of Internet Explorer and Edge Legacy (JsRT versions of Chakra, ActiveScript version of Chakra and Classic JavaScript Engine). +Project was based on the code of [SassAndCoffee.JavaScript](https://github.com/anaisbetts/SassAndCoffee), [Chakra Sample Hosts](https://github.com/panopticoncentral/chakra-host) and [jsrt-dotnet](https://github.com/robpaveza/jsrt-dotnet). \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Polyfills/System/Reflection/TypeInfoExtensions.cs b/src/MsieJavaScriptEngine/Polyfills/System/Reflection/TypeInfoExtensions.cs new file mode 100644 index 0000000..16d267c --- /dev/null +++ b/src/MsieJavaScriptEngine/Polyfills/System/Reflection/TypeInfoExtensions.cs @@ -0,0 +1,25 @@ +#if NETSTANDARD1_3 +using System; +using System.Reflection; + +namespace MsieJavaScriptEngine.Polyfills.System.Reflection +{ + internal static class TypeInfoExtensions + { + public static bool IsInstanceOfType(this TypeInfo source, object o) + { + if (source is null) + { + throw new ArgumentNullException(nameof(source)); + } + + if (o is null) + { + return false; + } + + return source.IsAssignableFrom(o.GetType().GetTypeInfo()); + } + } +} +#endif \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/PrecompiledScript.cs b/src/MsieJavaScriptEngine/PrecompiledScript.cs new file mode 100644 index 0000000..adb00e6 --- /dev/null +++ b/src/MsieJavaScriptEngine/PrecompiledScript.cs @@ -0,0 +1,60 @@ +namespace MsieJavaScriptEngine +{ + /// + /// Represents a pre-compiled script that can be executed by different instances of the MSIE JS engine + /// + public sealed class PrecompiledScript + { + /// + /// Gets a name of JS engine mode + /// + public string EngineMode + { + get; + private set; + } + + /// + /// Gets a source code of the script + /// + internal string Code + { + get; + private set; + } + + /// + /// Gets a cached data for accelerated recompilation + /// + internal byte[] CachedBytes + { + get; + private set; + } + + /// + /// Gets a document name + /// + internal string DocumentName + { + get; + private set; + } + + + /// + /// Constructs an instance of pre-compiled script + /// + /// Name of JS engine mode + /// The source code of the script + /// Cached data for accelerated recompilation + /// Document name + internal PrecompiledScript(string engineMode, string code, byte[] cachedBytes, string documentName) + { + EngineMode = engineMode; + Code = code; + CachedBytes = cachedBytes; + DocumentName = documentName; + } + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Properties/AssemblyInfo.cs b/src/MsieJavaScriptEngine/Properties/AssemblyInfo.cs index dc94e48..2820a6c 100644 --- a/src/MsieJavaScriptEngine/Properties/AssemblyInfo.cs +++ b/src/MsieJavaScriptEngine/Properties/AssemblyInfo.cs @@ -1,19 +1,8 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("MsieJavaScriptEngine")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("MSIE JavaScript Engine")] -[assembly: AssemblyCopyright("Copyright © 2012-2016 Andrey Taritsyn")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("ae6911c9-e2a9-4386-ab90-3722a9166564")] - -[assembly: AssemblyVersion("1.7.0.0")] -[assembly: AssemblyFileVersion("1.7.0.0")] - -[module: DefaultCharSet(CharSet.Unicode)] \ No newline at end of file +using System.Runtime.CompilerServices; + +// Unit tests need access to internal members +[assembly: InternalsVisibleTo("MsieJavaScriptEngine.Test.Common, PublicKey=002400000480000094000000060200000024000052534131000400000100010031605c3b70d76b" + +"bb7e2afeccded26c52f66c4c7047c7a5b6a88301f54f1e91d5e5a08a9e36b0ad18a403f7894eda" + +"d58b014576963d6c6073e3df92eebadd1e9c6bc6c8fb3ad8b95e6358a0948a2fc5df0fa76553b5" + +"63c7584fc6fa22952bd24965f64877622d14559a3603879f8a746eaed0f2cb009dec5537770d94" + +"83e629a9")] \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Resources/CommonStrings.Designer.cs b/src/MsieJavaScriptEngine/Resources/CommonStrings.Designer.cs new file mode 100644 index 0000000..07f2d33 --- /dev/null +++ b/src/MsieJavaScriptEngine/Resources/CommonStrings.Designer.cs @@ -0,0 +1,563 @@ +//------------------------------------------------------------------------------ +// +// 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 MsieJavaScriptEngine.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 CommonStrings + { + private static Lazy _resourceManager = + new Lazy(() => new ResourceManager( + "MsieJavaScriptEngine.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 + /// + 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 "The parameter '{0}' must have a `{1}` type." + /// + internal static string Common_ArgumentHasIncorrectType + { + get { return GetString("Common_ArgumentHasIncorrectType"); } + } + + /// + /// Looks up a localized string similar to "The parameter '{0}' must be a non-empty string." + /// + internal static string Common_ArgumentIsEmpty + { + get { return GetString("Common_ArgumentIsEmpty"); } + } + + /// + /// Looks up a localized string similar to "The parameter '{0}' must be a non-nullable." + /// + internal 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}`." + /// + internal static string Common_CannotConvertObjectToType + { + get { return GetString("Common_CannotConvertObjectToType"); } + } + + /// + /// Looks up a localized string similar to "File '{0}' not exist." + /// + internal static string Common_FileNotExist + { + get { return GetString("Common_FileNotExist"); } + } + + /// + /// Looks up a localized string similar to "Resource with name '{0}' is null." + /// + internal static string Common_ResourceIsNull + { + get { return GetString("Common_ResourceIsNull"); } + } + + /// + /// Looks up a localized string similar to "See the original error message: “{0}”." + /// + internal static string Common_SeeOriginalErrorMessage + { + get { return GetString("Common_SeeOriginalErrorMessage"); } + } + + /// + /// Looks up a localized string similar to "Cannot convert null to a value type." + /// + internal static string Common_ValueTypeCannotBeNull + { + get { return GetString("Common_ValueTypeCannotBeNull"); } + } + + /// + /// Looks up a localized string similar to "Most likely it happened, because the '{0}' assembly was not registered in your system." + /// + internal static string Engine_AssemblyNotRegistered + { + get { return GetString("Engine_AssemblyNotRegistered"); } + } + + /// + /// Looks up a localized string similar to "Try to install the Windows 10 with Edge Legacy browser." + /// + internal static string Engine_EdgeInstallationRequired + { + get { return GetString("Engine_EdgeInstallationRequired"); } + } + + /// + /// Looks up a localized string similar to "Try to install the Internet Explorer {0} or higher." + /// + internal static string Engine_IeInstallationRequired + { + get { return GetString("Engine_IeInstallationRequired"); } + } + + /// + /// Looks up a localized string similar to "Failed to create instance of the MsieJsEngine in {0} mode." + /// + internal static string Engine_JsEngineNotLoaded + { + get { return GetString("Engine_JsEngineNotLoaded"); } + } + + /// + /// 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 "Call stack" + /// + internal static string ErrorDetails_CallStack + { + get { return GetString("ErrorDetails_CallStack"); } + } + + /// + /// Looks up a localized string similar to "Category" + /// + internal static string ErrorDetails_Category + { + get { return GetString("ErrorDetails_Category"); } + } + + /// + /// Looks up a localized string similar to "Column number" + /// + internal static string ErrorDetails_ColumnNumber + { + get { return GetString("ErrorDetails_ColumnNumber"); } + } + + /// + /// Looks up a localized string similar to "Description" + /// + internal static string ErrorDetails_Description + { + get { return GetString("ErrorDetails_Description"); } + } + + /// + /// Looks up a localized string similar to "Document name" + /// + internal static string ErrorDetails_DocumentName + { + get { return GetString("ErrorDetails_DocumentName"); } + } + + /// + /// Looks up a localized string similar to "Engine mode" + /// + internal static string ErrorDetails_EngineMode + { + get { return GetString("ErrorDetails_EngineMode"); } + } + + /// + /// Looks up a localized string similar to "Line number" + /// + internal static string ErrorDetails_LineNumber + { + get { return GetString("ErrorDetails_LineNumber"); } + } + + /// + /// Looks up a localized string similar to "Message" + /// + internal static string ErrorDetails_Message + { + get { return GetString("ErrorDetails_Message"); } + } + + /// + /// Looks up a localized string similar to "Source fragment" + /// + internal static string ErrorDetails_SourceFragment + { + get { return GetString("ErrorDetails_SourceFragment"); } + } + + /// + /// Looks up a localized string similar to "Type" + /// + internal 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." + /// + internal static string Runtime_FunctionNotExist + { + get { return GetString("Runtime_FunctionNotExist"); } + } + + /// + /// 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 "The '{0}' line of the script call stack has an incorrect format." + /// + internal static string Runtime_InvalidCallStackLineFormat + { + get { return GetString("Runtime_InvalidCallStackLineFormat"); } + } + + /// + /// 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 "Script execution was interrupted." + /// + internal static string Runtime_ScriptInterrupted + { + get { return GetString("Runtime_ScriptInterrupted"); } + } + + /// + /// 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 "Cannot execute a '{0}' file, because it is empty." + /// + internal 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." + /// + internal 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 mode with name `{0}`." + /// + internal static string Usage_CannotExecutePrecompiledScriptForAnotherJsEngineMode + { + get { return GetString("Usage_CannotExecutePrecompiledScriptForAnotherJsEngineMode"); } + } + + /// + /// Looks up a localized string similar to "Cannot pre-compile a '{0}' file, because it is empty." + /// + internal 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." + /// + internal 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." + /// + internal static string Usage_EmbeddedHostObjectTypeNotSupported + { + get { return GetString("Usage_EmbeddedHostObjectTypeNotSupported"); } + } + + /// + /// Looks up a localized string similar to "The embedded host type `{0}` is not supported." + /// + internal 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." + /// + internal static string Usage_FunctionParameterTypeNotSupported + { + get { return GetString("Usage_FunctionParameterTypeNotSupported"); } + } + + /// + /// Looks up a localized string similar to "The document name '{0}' has incorrect format." + /// + internal static string Usage_InvalidDocumentNameFormat + { + get { return GetString("Usage_InvalidDocumentNameFormat"); } + } + + /// + /// Looks up a localized string similar to "The file name '{0}' has incorrect format." + /// + internal static string Usage_InvalidFileNameFormat + { + get { return GetString("Usage_InvalidFileNameFormat"); } + } + + /// + /// Looks up a localized string similar to "The function name '{0}' has incorrect format." + /// + internal static string Usage_InvalidFunctionNameFormat + { + get { return GetString("Usage_InvalidFunctionNameFormat"); } + } + + /// + /// Looks up a localized string similar to "The resource name '{0}' has incorrect format." + /// + internal static string Usage_InvalidResourceNameFormat + { + get { return GetString("Usage_InvalidResourceNameFormat"); } + } + + /// + /// Looks up a localized string similar to "The script item name '{0}' has incorrect format." + /// + internal static string Usage_InvalidScriptItemNameFormat + { + get { return GetString("Usage_InvalidScriptItemNameFormat"); } + } + + /// + /// Looks up a localized string similar to "The variable name '{0}' has incorrect format." + /// + internal static string Usage_InvalidVariableNameFormat + { + get { return GetString("Usage_InvalidVariableNameFormat"); } + } + + /// + /// Looks up a localized string similar to "Selected '{0}' mode of JavaScript engine is not supported." + /// + internal static string Usage_JsEngineModeNotSupported + { + get { return GetString("Usage_JsEngineModeNotSupported"); } + } + + /// + /// Looks up a localized string similar to "It is prohibited to use the {0} and {1} engines in one process." + /// + internal static string Usage_JsEnginesConflictInProcess + { + get { return GetString("Usage_JsEnginesConflictInProcess"); } + } + + /// + /// Looks up a localized string similar to "The type of return value `{0}` is not supported." + /// + internal 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." + /// + internal static string Usage_VariableTypeNotSupported + { + get { return GetString("Usage_VariableTypeNotSupported"); } + } + + private static string GetString(string name) + { + string value = ResourceManager.GetString(name, _resourceCulture); + + return value; + } + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Resources/Strings.resx b/src/MsieJavaScriptEngine/Resources/CommonStrings.resx similarity index 54% rename from src/MsieJavaScriptEngine/Resources/Strings.resx rename to src/MsieJavaScriptEngine/Resources/CommonStrings.resx index a9b9aca..e950347 100644 --- a/src/MsieJavaScriptEngine/Resources/Strings.resx +++ b/src/MsieJavaScriptEngine/Resources/CommonStrings.resx @@ -117,6 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + The parameter '{0}' must have a `{1}` type. + The parameter '{0}' must be a non-empty string. @@ -126,30 +129,51 @@ Cannot convert object of type `{0}` to type `{1}`. - - Serialization of type `{0}` is not supported. - File '{0}' not exist. - - Value cannot be null. + + Resource with name '{0}' is null. + + + See the original error message: “{0}”. Cannot convert null to a value type. + + Most likely it happened, because the '{0}' assembly was not registered in your system. + + + Try to install the Windows 10 with Edge Legacy browser. + + + Try to install the Internet Explorer {0} or higher. + + + Failed to create instance of the MsieJsEngine in {0} mode. + + + Maximum stack size must be non-negative. + + + Call stack + Category Column number + + Description + + + Document name + Engine mode - - Error code - Line number @@ -159,94 +183,124 @@ Source fragment - - Resource with name '{0}' is null. + + Type - - ActiveScript dispatcher is not initialized. + + The function with the name '{0}' does not exist. - - Failed to set '{0}' version of script language for the ActiveScript JavaScript engine. + + During invocation of the host delegate an error has occurred - “{0}”. - - Failed to load a Chakra Edge JsRT JavaScript engine. Try to install the Windows 10 with Edge browser. -See more details: - -{0} + + During getting value of '{0}' field of the host object an error has occurred - “{1}”. - - The embedded host object '{0}' has a type `{1}`, which is not supported. + + During setting value of '{0}' field of the host object an error has occurred - “{1}”. - - The embedded host type `{0}` is not supported. + + During invocation of '{0}' method of the host object an error has occurred - “{1}”. - - The function name '{0}' is forbidden, as is contained in the list of reserved words of JavaScript language. + + During getting value of '{0}' property of the host object an error has occurred - “{1}”. - - The function with the name '{0}' does not exist. + + During setting value of '{0}' property of the host object an error has occurred - “{1}”. - - One of the function parameters '{0}' has a type `{1}`, which is not supported. + + During invocation of constructor of the `{0}` host type an error has occurred - “{1}”. - - Failed to load a {0} JavaScript engine. Try to install the Internet Explorer {1} or higher. -See more details: - -{2} + + Could not create instance of the `{0}` host type, because it does not have any public constructor. - - The function name '{0}' has incorrect format. + + During getting value of '{0}' field of the `{1}` host type an error has occurred - “{2}”. - - The parser you passed implements neither `IActiveScriptParse32` nor `IActiveScriptParse64`. + + During setting value of '{0}' field of the `{1}` host type an error has occurred - “{2}”. - - Property name '{0}' has incorrect format. + + During invocation of '{0}' method of the `{1}` host type an error has occurred - “{2}”. - - The script item name '{0}' has incorrect format. + + During getting value of '{0}' property of the `{1}` host type an error has occurred - “{2}”. - - The variable name '{0}' has incorrect format. + + During setting value of '{0}' property of the host type `{1}` an error has occurred - “{2}”. - - Item with name '{0}' not found. + + The '{0}' line of the script call stack has an incorrect format. - - Selected '{0}' mode of JavaScript engine is not supported! + + Could not retrieve field '{0}' of the host object, because there was an invalid `this` context. - - It is prohibited to use the {0} and {1} engines in one process. + + Could not call method '{0}' of the host object, because there was an invalid `this` context. - - It is prohibited to use the Chakra JsRT and Chakra ActiveScript engines on one machine at a time. + + Could not retrieve property '{0}' of the host object, because there was an invalid `this` context. - - Could not found none of the JavaScript engines. Perhaps you have not installed the Internet Explorer browser. + + Script execution was interrupted. - - Property name '{0}' is forbidden, as one of its parts matches with the reserved word of JavaScript language. + + Could not find suitable constructor or not enough arguments to invoke of constructor of the `{0}` host type. - - The property '{1}' of variable '{0}' does not exist. + + Could not find suitable method or not enough arguments to invoke of '{0}' method of the host object. - - The property '{1}' of variable '{0}' has a type `{2}`, which is not supported. + + Cannot execute a '{0}' file, because it is empty. - - The type of return value `{0}` is not supported. + + Cannot execute a '{0}' resource, because it is empty. + + + Cannot execute a pre-compiled script, because it was created for another mode 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. + + + One of the function parameters '{0}' has a type `{1}`, which is not supported. + + + The document name '{0}' has incorrect format. + + + The file name '{0}' has incorrect format. + + + The function name '{0}' has incorrect format. + + + The resource name '{0}' has incorrect format. - - Cannot convert to `{0}`. The type is unsupported. + + The script item name '{0}' has incorrect format. + + + The variable name '{0}' has incorrect format. - - The variable name '{0}' is forbidden, as is contained in the list of reserved words of JavaScript language. + + Selected '{0}' mode of JavaScript engine is not supported. - - The variable with the name '{0}' does not exist. + + It is prohibited to use the {0} and {1} engines in one process. + + + The type of return value `{0}` is not supported. - + The variable '{0}' has a type `{1}`, which is not supported. \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Resources/Strings.ru-ru.Designer.cs b/src/MsieJavaScriptEngine/Resources/CommonStrings.ru-RU.Designer.cs similarity index 100% rename from src/MsieJavaScriptEngine/Resources/Strings.ru-ru.Designer.cs rename to src/MsieJavaScriptEngine/Resources/CommonStrings.ru-RU.Designer.cs diff --git a/src/MsieJavaScriptEngine/Resources/Strings.ru-ru.resx b/src/MsieJavaScriptEngine/Resources/CommonStrings.ru-RU.resx similarity index 50% rename from src/MsieJavaScriptEngine/Resources/Strings.ru-ru.resx rename to src/MsieJavaScriptEngine/Resources/CommonStrings.ru-RU.resx index cc53d1f..76124ab 100644 --- a/src/MsieJavaScriptEngine/Resources/Strings.ru-ru.resx +++ b/src/MsieJavaScriptEngine/Resources/CommonStrings.ru-RU.resx @@ -117,6 +117,9 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Параметр с именем "{0}" должен иметь тип `{1}`! + Параметр с именем "{0}" не должен содержать пустую строку! @@ -126,30 +129,51 @@ Невозможно преобразовать объект типа `{0}` в тип `{1}`! - - Сериализация типа `{0}` не поддерживается! - Файл "{0}" не существует! - - Значение не должно быть равно null! + + Ресурс с именем "{0}" содержит значение равное null! + + + Смотри оригинальное сообщение об ошибке: «{0}». Невозможно преобразовать null в значимый тип! + + Скорее всего, это произошло, потому что сборка "{0}" не была зарегистрирована в вашей системе. + + + Попробуйте установить Windows 10 с веб-браузером Edge Legacy. + + + Попробуйте установить Internet Explorer {0} или выше. + + + Не удалось создать экземпляр MsieJsEngine в режиме {0}. + + + Максимальный размер стека должен быть неотрицательным! + + + Стек вызовов + Категория Номер столбца + + Описание + + + Имя документа + Режим движка - - Код ошибки - Номер строки @@ -159,94 +183,124 @@ Фрагмент исходного кода - - Ресурс с именем "{0}" содержит значение равное null! + + Тип - - ActiveScript-диспетчер не инициализирован! + + Функция с именем "{0}" не существует! - - Не удалось установить "{0}" версию языка сценариев для ActiveScript JavaScript-движка! + + Во время вызова делегата хоста произошла ошибка - «{0}»! - - Не удалось загрузить Chakra Edge JsRT JavaScript-движок! Попробуйте установить Windows 10 с веб-браузером Edge. -Смотрите более подробную информацию об ошибке: - -{0} + + Во время получения значения поля "{0}" объекта хоста произошла ошибка - «{1}»! - - Встраиваемый объекта хоста "{0}" имеет тип `{1}`, который не поддерживается! + + Во время присваивания значения полю "{0}" объекта хоста произошла ошибка - «{1}»! - - Встраиваемый тип хоста `{0}` не поддерживается! + + Во время вызова метода "{0}" объекта хоста произошла ошибка - «{1}»! - - Имя функции "{0}" запрещено, т.к. входит в список зарезервированных слов JavaScript! + + Во время получения значения свойства "{0}" объекта хоста произошла ошибка - «{1}»! - - Функция с именем "{0}" не существует! + + Во время присваивания значения свойству "{0}" объекта хоста произошла ошибка - «{1}»! - - Один из параметров функции "{0}" имеет тип `{1}`, который не поддерживается! + + Во время вызова конструктора типа хоста `{0}` произошла ошибка - «{1}»! - - Не удалось загрузить {0} JavaScript-движок! Попробуйте установить Internet Explorer {1} или выше. -Смотрите более подробную информацию об ошибке: - -{2} + + Не удается создать экземпляр типа хоста `{0}`, потому что он не имеет ни одного публичного конструктора! - - Имя функции "{0}" имеет некорректный формат! + + Во время получения значения поля "{0}" типа хоста `{1}` произошла ошибка - «{2}»! - - Парсер, переданный вами, не реализует интерфейсы `IActiveScriptParse32` и `IActiveScriptParse64`! + + Во время присваивания значения полю "{0}" типа хоста `{1}` произошла ошибка - «{2}»! - - Имя свойства "{0}" имеет некорректный формат! + + Во время вызова метода "{0}" типа хоста `{1}` произошла ошибка - «{2}»! - - Имя скриптового элемента "{0}" имеет некорректный формат! + + Во время получения значения свойства "{0}" типа хоста `{1}` произошла ошибка - «{2}»! - - Имя переменной "{0}" имеет некорректный формат! + + Во время присваивания значения свойству "{0}" типа хоста `{1}` произошла ошибка - «{2}»! - - Элемент с именем "{0}" не найден! + + Строка стека вызовов скрипта "{0}" имеет некорректный формат! - - Выбранный вами режим JavaScript-движка "{0}" не поддерживается! + + Не удалось получить поле "{0}" объекта хоста, потому что контекст `this` недействителен! - - Нельзя использовать {0} и {1} движки в одном процессе. + + Не удалось вызвать метод "{0}" объекта хоста, потому что контекст `this` недействителен! - - Нельзя использовать Chakra JsRT и Chakra ActiveScript движки на одном компьютере одновременно. + + Не удалось получить свойство "{0}" объекта хоста, потому что контекст `this` недействителен! - - Не удалось найти ни один из JavaScript-движков! Возможно, на вашем компьютере не установлен браузер Internet Explorer. + + Выполнение скрипта было прервано! - - Имя свойства "{0}" запрещено, т.к. одна из его частей совпадает с зарезервированным словом JavaScript! + + Не получается найти подходящий конструктор или не хватает аргументов для вызова конструктора `{0}` типа хоста! - - Свойство "{1}" переменной "{0}" не существует! + + Не получается найти подходящий метод или не хватает аргументов для вызова метода "{0}" объекта хоста! - - Значение свойства "{1}" переменной "{0}" имеет тип `{2}`, который не поддерживается! + + Нельзя выполнить файл "{0}", потому что он пустой! - - Тип возвращаемого значения `{0}` не поддерживается! + + Нельзя выполнить ресурс "{0}", потому что он пустой! + + + Нельзя выполнить предварительно скомпилированный сценарий, потому что он был создан для другого режима с именем `{0}`! + + + Нельзя произвести предварительную компиляцию файла "{0}", потому что он пустой! + + + Нельзя произвести предварительную компиляцию ресурса "{0}", потому что он пустой! + + + Встраиваемый объекта хоста "{0}" имеет тип `{1}`, который не поддерживается! + + + Встраиваемый тип хоста `{0}` не поддерживается! + + + Один из параметров функции "{0}" имеет тип `{1}`, который не поддерживается! - - Невозможно привести значение к типу `{0}`! Данный тип не поддерживается. + + Имя документа "{0}" имеет некорректный формат! - - Имя переменной "{0}" запрещено, т.к. входит в список зарезервированных слов JavaScript! + + Имя файла "{0}" имеет некорректный формат! - - Переменная с именем "{0}" не существует! + + Имя функции "{0}" имеет некорректный формат! + + + Имя ресурса "{0}" имеет некорректный формат! + + + Имя скриптового элемента "{0}" имеет некорректный формат! + + + Имя переменной "{0}" имеет некорректный формат! + + + Выбранный вами режим JavaScript-движка "{0}" не поддерживается! + + + Нельзя использовать {0} и {1} движки в одном процессе. + + + Тип возвращаемого значения `{0}` не поддерживается! - + Переменная "{0}" имеет тип `{1}`, который не поддерживается! \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Resources/ES5.js b/src/MsieJavaScriptEngine/Resources/ES5.js index 9ff4f4c..821c180 100644 --- a/src/MsieJavaScriptEngine/Resources/ES5.js +++ b/src/MsieJavaScriptEngine/Resources/ES5.js @@ -1,8 +1,8 @@ /*! * This polyfill based on code of the following libraries: -* 1. Douglas Crockford's ECMAScript 5 Polyfill v0.1 - http://nuget.org/packages/ES5 -* 2. MDN JavaScript Polyfills - http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference -* 3. Steven Levithan's Cross-Browser Split v1.1.1 - http://blog.stevenlevithan.com/archives/cross-browser-split +* 1. Douglas Crockford's ECMAScript 5 Polyfill v0.1 - https://www.nuget.org/packages/ES5 +* 2. MDN JavaScript Polyfills - https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference +* 3. Steven Levithan's Cross-Browser Split v1.1.1 - https://blog.stevenlevithan.com/archives/cross-browser-split */ (function (undefined) { diff --git a/src/MsieJavaScriptEngine/Resources/ES5.min.js b/src/MsieJavaScriptEngine/Resources/ES5.min.js deleted file mode 100644 index 60798cb..0000000 --- a/src/MsieJavaScriptEngine/Resources/ES5.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! -* This polyfill based on code of the following libraries: -* 1. Douglas Crockford's ECMAScript 5 Polyfill v0.1 - http://nuget.org/packages/ES5 -* 2. MDN JavaScript Polyfills - http://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference -* 3. Steven Levithan's Cross-Browser Split v1.1.1 - http://blog.stevenlevithan.com/archives/cross-browser-split -*/ -(function(n){function a(n){return h(n)==="[object Array]"}function o(n){return typeof n=="number"}function u(n){return n===void 0}function v(n){return s(n)&&h(n)==="[object RegExp]"}function s(n){return typeof n=="object"&&n!==null}function t(n){return typeof n=="function"}function h(n){return Object.prototype.toString.call(n)}var c="argument is not an Object.",i="argument is not a Function object.",l="implementation only accepts one parameter.",f="reduce of empty array with no initial value.",e,r;Array.prototype.hasOwnProperty("every")||(Array.prototype.every=function(n,r){var f=this,u,e=f.length;if(!t(n))throw new TypeError("Array.prototype.every: "+i);for(u=0;u=r)return-1;i<0&&(i=r-Math.abs(i))}else i=0;while(i=r)return-1;i<0&&(i=r-Math.abs(i))}else i=r-1;while(i>=0){if(u.hasOwnProperty(i)&&u[i]===n)return i;i-=1}return-1});Array.prototype.hasOwnProperty("map")||(Array.prototype.map=function(n,r){var f=this,u,o=f.length,e;if(!t(n))throw new TypeError("Array.prototype.map: "+i);for(e=new Array(o),u=0;u1&&(o=u,h=!0),e=0;l>e;e+=1)s.hasOwnProperty(e)&&(h?o=r.call(n,o,s[e],e,s):(o=this[e],h=!0));if(!h)throw new TypeError(c+": "+f);return o});Array.prototype.hasOwnProperty("reduceRight")||(Array.prototype.reduceRight=function(r,u){var o=this,e,l=o.length,s=null,h=!1,c="Array.prototype.reduceRight";if(!t(r))throw new TypeError(c+": "+i);for(arguments.length>1&&(s=u,h=!0),e=l-1;e>=0;e-=1)o.hasOwnProperty(e)&&(h?s=r.call(n,s,o[e],e,o):(s=o[e],h=!0));if(!h)throw new TypeError(c+": "+f);return s});Array.prototype.hasOwnProperty("some")||(Array.prototype.some=function(n,r){var f=this,u,e=f.length;if(!t(n))throw new TypeError("Array.prototype.some: "+i);for(u=0;u1)throw new Error("Object.create: "+l);return t.prototype=n,new t});Object.hasOwnProperty("keys")||(Object.keys=function(n){var i,r=[];if(!s(n)&&!t(n))throw new TypeError("Object.keys: "+c);for(i in n)Object.prototype.hasOwnProperty.call(n,i)&&r.push(i);return r});("aa".split(/a/).length===0||"|a|".split(/\|/).length===1||"1, 2".split(/\s*(,)/).length===2)&&(e=String.prototype.split,r=u(/()??/.exec("")[1]),String.prototype.split=function(t,i){var s=this,o,a,h,c,p,f,y,w,l,b;if(!v(t))return e.call(s,t,i);for(o=[],a=(t.ignoreCase?"i":"")+(t.multiline?"m":"")+(t.extended?"x":"")+(t.sticky?"y":""),h=0,c=new RegExp(t.source,a+"g"),r||(p=new RegExp("^"+c.source+"$(?!\\s)",a)),i=u(i)?-1>>>0:i>>>0;f=c.exec(s);){if(y=f.index+f[0].length,y>h&&(o.push(s.slice(h,f.index)),!r&&f.length>1&&f[0].replace(p,function(){for(l=1,b=arguments.length;l1&&f.index=i))break;c.lastIndex===f.index&&c.lastIndex++}return h===s.length?(w||!c.test(""))&&o.push(""):o.push(s.slice(h)),o.length>i?o.slice(0,i):o});String.prototype.hasOwnProperty("trim")||(String.prototype.trim=function(n){return function(){return this.replace(n,"$1")}}(/^\s*([\s\S]*\S)?\s*$/))})() \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Resources/NetCoreStrings.Designer.cs b/src/MsieJavaScriptEngine/Resources/NetCoreStrings.Designer.cs new file mode 100644 index 0000000..21c4dc4 --- /dev/null +++ b/src/MsieJavaScriptEngine/Resources/NetCoreStrings.Designer.cs @@ -0,0 +1,83 @@ +//------------------------------------------------------------------------------ +// +// 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 MsieJavaScriptEngine.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 NetCoreStrings + { + private static Lazy _resourceManager = + new Lazy(() => new ResourceManager( + "MsieJavaScriptEngine.Resources.NetCoreStrings", +#if NET20 || NET30 || NET35 || NET40 + typeof(NetCoreStrings).Assembly +#else + typeof(NetCoreStrings).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 "Could not found none of the JavaScript engines, which would be compatible with .NET Core. Perhaps..." + /// + internal static string Engine_JsEnginesNotFound + { + get { return GetString("Engine_JsEnginesNotFound"); } + } + + /// + /// Looks up a localized string similar to "The '{0}' mode of JavaScript engine is not compatible with .NET Core." + /// + internal static string Usage_JsEngineModeNotCompatibleWithNetCore + { + get { return GetString("Usage_JsEngineModeNotCompatibleWithNetCore"); } + } + + private static string GetString(string name) + { + string value = ResourceManager.GetString(name, _resourceCulture); + + return value; + } + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Resources/NetCoreStrings.resx b/src/MsieJavaScriptEngine/Resources/NetCoreStrings.resx new file mode 100644 index 0000000..e969324 --- /dev/null +++ b/src/MsieJavaScriptEngine/Resources/NetCoreStrings.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + Could not found none of the JavaScript engines, which would be compatible with .NET Core. Perhaps you have not installed the Microsoft Edge Legacy or Internet Explorer 11 browser. + + + The '{0}' mode of JavaScript engine is not compatible with .NET Core. + + \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Resources/NetCoreStrings.ru-RU.Designer.cs b/src/MsieJavaScriptEngine/Resources/NetCoreStrings.ru-RU.Designer.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/MsieJavaScriptEngine/Resources/NetCoreStrings.ru-RU.resx b/src/MsieJavaScriptEngine/Resources/NetCoreStrings.ru-RU.resx new file mode 100644 index 0000000..5835a08 --- /dev/null +++ b/src/MsieJavaScriptEngine/Resources/NetCoreStrings.ru-RU.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + Не удалось найти ни один из JavaScript-движков, который был бы совместим с .NET Core! Возможно, на вашем компьютере не установлен браузер Microsoft Edge Legacy или Internet Explorer 11. + + + Режим JavaScript-движка "{0}" не совместим с .NET Core! + + \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Resources/NetFrameworkStrings.Designer.cs b/src/MsieJavaScriptEngine/Resources/NetFrameworkStrings.Designer.cs new file mode 100644 index 0000000..b5404bc --- /dev/null +++ b/src/MsieJavaScriptEngine/Resources/NetFrameworkStrings.Designer.cs @@ -0,0 +1,107 @@ +//------------------------------------------------------------------------------ +// +// 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 MsieJavaScriptEngine.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 NetFrameworkStrings + { + private static Lazy _resourceManager = + new Lazy(() => new ResourceManager( + "MsieJavaScriptEngine.Resources.NetFrameworkStrings", +#if NET20 || NET30 || NET35 || NET40 + typeof(NetFrameworkStrings).Assembly +#else + typeof(NetFrameworkStrings).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 "ActiveScript dispatcher is not initialized." + /// + internal static string Engine_ActiveScriptDispatcherNotInitialized + { + get { return GetString("Engine_ActiveScriptDispatcherNotInitialized"); } + } + + /// + /// Looks up a localized string similar to "Failed to set '{0}' version of script language for the ActiveScript JavaScript engine." + /// + internal static string Engine_ActiveScriptLanguageVersionSelectionFailed + { + get { return GetString("Engine_ActiveScriptLanguageVersionSelectionFailed"); } + } + + /// + /// Looks up a localized string similar to "Could not found none of the JavaScript engines. Perhaps you have not installed the Internet Explorer..." + /// + internal static string Engine_JsEnginesNotFound + { + get { return GetString("Engine_JsEnginesNotFound"); } + } + + /// + /// Looks up a localized string similar to "Item with name '{0}' not found." + /// + internal static string Runtime_ItemNotFound + { + get { return GetString("Runtime_ItemNotFound"); } + } + + /// + /// Looks up a localized string similar to "The variable with the name '{0}' does not exist." + /// + internal static string Runtime_VariableNotExist + { + get { return GetString("Runtime_VariableNotExist"); } + } + + private static string GetString(string name) + { + string value = ResourceManager.GetString(name, _resourceCulture); + + return value; + } + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Resources/NetFrameworkStrings.resx b/src/MsieJavaScriptEngine/Resources/NetFrameworkStrings.resx new file mode 100644 index 0000000..3ce799f --- /dev/null +++ b/src/MsieJavaScriptEngine/Resources/NetFrameworkStrings.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + ActiveScript dispatcher is not initialized. + + + Failed to set '{0}' version of script language for the ActiveScript JavaScript engine. + + + Could not found none of the JavaScript engines. Perhaps you have not installed the Internet Explorer browser. + + + Item with name '{0}' not found. + + + The variable with the name '{0}' does not exist. + + \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Resources/NetFrameworkStrings.ru-RU.Designer.cs b/src/MsieJavaScriptEngine/Resources/NetFrameworkStrings.ru-RU.Designer.cs new file mode 100644 index 0000000..e69de29 diff --git a/src/MsieJavaScriptEngine/Resources/NetFrameworkStrings.ru-RU.resx b/src/MsieJavaScriptEngine/Resources/NetFrameworkStrings.ru-RU.resx new file mode 100644 index 0000000..14923d5 --- /dev/null +++ b/src/MsieJavaScriptEngine/Resources/NetFrameworkStrings.ru-RU.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 + + + ActiveScript-диспетчер не инициализирован! + + + Не удалось установить "{0}" версию языка сценариев для ActiveScript JavaScript-движка! + + + Не удалось найти ни один из JavaScript-движков! Возможно, на вашем компьютере не установлен браузер Internet Explorer. + + + Элемент с именем "{0}" не найден! + + + Переменная с именем "{0}" не существует! + + \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/Resources/Strings.Designer.cs b/src/MsieJavaScriptEngine/Resources/Strings.Designer.cs deleted file mode 100644 index e8a951e..0000000 --- a/src/MsieJavaScriptEngine/Resources/Strings.Designer.cs +++ /dev/null @@ -1,447 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace MsieJavaScriptEngine.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("MsieJavaScriptEngine.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 The parameter '{0}' must be a non-empty string.. - /// - internal 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.. - /// - internal static string Common_ArgumentIsNull { - get { - return ResourceManager.GetString("Common_ArgumentIsNull", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot convert object of type `{0}` to type `{1}`.. - /// - internal static string Common_CannotConvertObjectToType { - get { - return ResourceManager.GetString("Common_CannotConvertObjectToType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Serialization of type `{0}` is not supported.. - /// - internal static string Common_CannotSerializeType { - get { - return ResourceManager.GetString("Common_CannotSerializeType", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to File '{0}' not exist.. - /// - internal static string Common_FileNotExist { - get { - return ResourceManager.GetString("Common_FileNotExist", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Value cannot be null.. - /// - internal 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.. - /// - internal static string Common_ValueTypeCannotBeNull { - get { - return ResourceManager.GetString("Common_ValueTypeCannotBeNull", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Category. - /// - internal static string ErrorDetails_Category { - get { - return ResourceManager.GetString("ErrorDetails_Category", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Column number. - /// - internal static string ErrorDetails_ColumnNumber { - get { - return ResourceManager.GetString("ErrorDetails_ColumnNumber", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Engine mode. - /// - internal static string ErrorDetails_EngineMode { - get { - return ResourceManager.GetString("ErrorDetails_EngineMode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Error code. - /// - internal static string ErrorDetails_ErrorCode { - get { - return ResourceManager.GetString("ErrorDetails_ErrorCode", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Line number. - /// - internal static string ErrorDetails_LineNumber { - get { - return ResourceManager.GetString("ErrorDetails_LineNumber", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Message. - /// - internal static string ErrorDetails_Message { - get { - return ResourceManager.GetString("ErrorDetails_Message", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Source fragment. - /// - internal static string ErrorDetails_SourceFragment { - get { - return ResourceManager.GetString("ErrorDetails_SourceFragment", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Resource with name '{0}' is null.. - /// - internal static string Resources_ResourceIsNull { - get { - return ResourceManager.GetString("Resources_ResourceIsNull", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to ActiveScript dispatcher is not initialized.. - /// - internal static string Runtime_ActiveScriptDispatcherNotInitialized { - get { - return ResourceManager.GetString("Runtime_ActiveScriptDispatcherNotInitialized", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to set '{0}' version of script language for the ActiveScript JavaScript engine.. - /// - internal static string Runtime_ActiveScriptLanguageVersionSelectionFailed { - get { - return ResourceManager.GetString("Runtime_ActiveScriptLanguageVersionSelectionFailed", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to load a Chakra Edge JsRT JavaScript engine. Try to install the Windows 10 with Edge browser. - ///See more details: - /// - ///{0}. - /// - internal static string Runtime_EdgeJsEngineNotLoaded { - get { - return ResourceManager.GetString("Runtime_EdgeJsEngineNotLoaded", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The embedded host object '{0}' has a type `{1}`, which is not supported.. - /// - internal 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.. - /// - internal 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.. - /// - internal 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.. - /// - internal 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.. - /// - internal static string Runtime_FunctionParameterTypeNotSupported { - get { - return ResourceManager.GetString("Runtime_FunctionParameterTypeNotSupported", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Failed to load a {0} JavaScript engine. Try to install the Internet Explorer {1} or higher. - ///See more details: - /// - ///{2}. - /// - internal static string Runtime_IeJsEngineNotLoaded { - get { - return ResourceManager.GetString("Runtime_IeJsEngineNotLoaded", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The function name '{0}' has incorrect format.. - /// - internal static string Runtime_InvalidFunctionNameFormat { - get { - return ResourceManager.GetString("Runtime_InvalidFunctionNameFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The parser you passed implements neither `IActiveScriptParse32` nor `IActiveScriptParse64`.. - /// - internal static string Runtime_InvalidParserImplementationError { - get { - return ResourceManager.GetString("Runtime_InvalidParserImplementationError", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Property name '{0}' has incorrect format.. - /// - internal static string Runtime_InvalidPropertyNameFormat { - get { - return ResourceManager.GetString("Runtime_InvalidPropertyNameFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The script item name '{0}' has incorrect format.. - /// - internal 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.. - /// - internal static string Runtime_InvalidVariableNameFormat { - get { - return ResourceManager.GetString("Runtime_InvalidVariableNameFormat", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Item with name '{0}' not found.. - /// - internal static string Runtime_ItemNotFound { - get { - return ResourceManager.GetString("Runtime_ItemNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Selected '{0}' mode of JavaScript engine is not supported!. - /// - internal static string Runtime_JsEngineModeNotSupported { - get { - return ResourceManager.GetString("Runtime_JsEngineModeNotSupported", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to It is prohibited to use the {0} and {1} engines in one process.. - /// - internal static string Runtime_JsEnginesConflictInProcess { - get { - return ResourceManager.GetString("Runtime_JsEnginesConflictInProcess", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to It is prohibited to use the Chakra JsRT and Chakra ActiveScript engines on one machine at a time.. - /// - internal static string Runtime_JsEnginesConflictOnMachine { - get { - return ResourceManager.GetString("Runtime_JsEnginesConflictOnMachine", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Could not found none of the JavaScript engines. Perhaps you have not installed the Internet Explorer browser.. - /// - internal static string Runtime_JsEnginesNotFound { - get { - return ResourceManager.GetString("Runtime_JsEnginesNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Property name '{0}' is forbidden, as one of its parts matches with the reserved word of JavaScript language.. - /// - internal static string Runtime_PropertyNameIsForbidden { - get { - return ResourceManager.GetString("Runtime_PropertyNameIsForbidden", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The property '{1}' of variable '{0}' does not exist.. - /// - internal static string Runtime_PropertyNotExist { - get { - return ResourceManager.GetString("Runtime_PropertyNotExist", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The property '{1}' of variable '{0}' has a type `{2}`, which is not supported.. - /// - internal static string Runtime_PropertyTypeNotSupported { - get { - return ResourceManager.GetString("Runtime_PropertyTypeNotSupported", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The type of return value `{0}` is not supported.. - /// - internal static string Runtime_ReturnValueTypeNotSupported { - get { - return ResourceManager.GetString("Runtime_ReturnValueTypeNotSupported", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot convert to `{0}`. The type is unsupported.. - /// - internal static string Runtime_TypeUnsupported { - get { - return ResourceManager.GetString("Runtime_TypeUnsupported", 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.. - /// - internal static string Runtime_VariableNameIsForbidden { - get { - return ResourceManager.GetString("Runtime_VariableNameIsForbidden", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The variable with the name '{0}' does not exist.. - /// - internal static string Runtime_VariableNotExist { - get { - return ResourceManager.GetString("Runtime_VariableNotExist", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The variable '{0}' has a type `{1}`, which is not supported.. - /// - internal static string Runtime_VariableTypeNotSupported { - get { - return ResourceManager.GetString("Runtime_VariableTypeNotSupported", resourceCulture); - } - } - } -} diff --git a/src/MsieJavaScriptEngine/Resources/json2.js b/src/MsieJavaScriptEngine/Resources/json2.js index 5838457..b43526d 100644 --- a/src/MsieJavaScriptEngine/Resources/json2.js +++ b/src/MsieJavaScriptEngine/Resources/json2.js @@ -1,158 +1,146 @@ -/* - json2.js - 2015-05-03 - - Public Domain. - - NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. - - See http://www.JSON.org/js.html - - - This code should be minified before deployment. - See http://javascript.crockford.com/jsmin.html - - USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO - NOT CONTROL. - - - This file creates a global JSON object containing two methods: stringify - and parse. This file is provides the ES5 JSON capability to ES3 systems. - If a project might run on IE8 or earlier, then this file should be included. - This file does nothing on ES5 systems. - - JSON.stringify(value, replacer, space) - value any JavaScript value, usually an object or array. - - replacer an optional parameter that determines how object - values are stringified for objects. It can be a - function or an array of strings. - - space an optional parameter that specifies the indentation - of nested structures. If it is omitted, the text will - be packed without extra whitespace. If it is a number, - it will specify the number of spaces to indent at each - level. If it is a string (such as '\t' or ' '), - it contains the characters used to indent at each level. - - This method produces a JSON text from a JavaScript value. - - When an object value is found, if the object contains a toJSON - method, its toJSON method will be called and the result will be - stringified. A toJSON method does not serialize: it returns the - value represented by the name/value pair that should be serialized, - or undefined if nothing should be serialized. The toJSON method - will be passed the key associated with the value, and this will be - bound to the value - - For example, this would serialize Dates as ISO strings. - - Date.prototype.toJSON = function (key) { - function f(n) { - // Format integers to have at least two digits. - return n < 10 - ? '0' + n - : n; - } - - return this.getUTCFullYear() + '-' + - f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z'; - }; - - You can provide an optional replacer method. It will be passed the - key and value of each member, with this bound to the containing - object. The value that is returned from your method will be - serialized. If your method returns undefined, then the member will - be excluded from the serialization. - - If the replacer parameter is an array of strings, then it will be - used to select the members to be serialized. It filters the results - such that only members with keys listed in the replacer array are - stringified. - - Values that do not have JSON representations, such as undefined or - functions, will not be serialized. Such values in objects will be - dropped; in arrays they will be replaced with null. You can use - a replacer function to replace those with JSON values. - JSON.stringify(undefined) returns undefined. - - The optional space parameter produces a stringification of the - value that is filled with line breaks and indentation to make it - easier to read. - - If the space parameter is a non-empty string, then that string will - be used for indentation. If the space parameter is a number, then - the indentation will be that many spaces. - - Example: - - text = JSON.stringify(['e', {pluribus: 'unum'}]); - // text is '["e",{"pluribus":"unum"}]' - - - text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t'); - // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' - - text = JSON.stringify([new Date()], function (key, value) { - return this[key] instanceof Date - ? 'Date(' + this[key] + ')' - : value; - }); - // text is '["Date(---current time---)"]' - - - JSON.parse(text, reviver) - This method parses a JSON text to produce an object or array. - It can throw a SyntaxError exception. - - The optional reviver parameter is a function that can filter and - transform the results. It receives each of the keys and values, - and its return value is used instead of the original value. - If it returns what it received, then the structure is not modified. - If it returns undefined then the member is deleted. - - Example: - - // Parse the text. Values that look like ISO date strings will - // be converted to Date objects. - - myData = JSON.parse(text, function (key, value) { - var a; - if (typeof value === 'string') { - a = -/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); - if (a) { - return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], - +a[5], +a[6])); - } - } - return value; - }); - - myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) { - var d; - if (typeof value === 'string' && - value.slice(0, 5) === 'Date(' && - value.slice(-1) === ')') { - d = new Date(value.slice(5, -1)); - if (d) { - return d; - } - } - return value; - }); - - - This is a reference implementation. You are free to copy, modify, or - redistribute. -*/ - -/*jslint - eval, for, this +// json2.js +// 2023-05-10 +// Public Domain. +// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK. + +// USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO +// NOT CONTROL. + +// This file creates a global JSON object containing two methods: stringify +// and parse. This file provides the ES5 JSON capability to ES3 systems. +// If a project might run on IE8 or earlier, then this file should be included. +// This file does nothing on ES5 systems. + +// JSON.stringify(value, replacer, space) +// value any JavaScript value, usually an object or array. +// replacer an optional parameter that determines how object +// values are stringified for objects. It can be a +// function or an array of strings. +// space an optional parameter that specifies the indentation +// of nested structures. If it is omitted, the text will +// be packed without extra whitespace. If it is a number, +// it will specify the number of spaces to indent at each +// level. If it is a string (such as "\t" or " "), +// it contains the characters used to indent at each level. +// This method produces a JSON text from a JavaScript value. +// When an object value is found, if the object contains a toJSON +// method, its toJSON method will be called and the result will be +// stringified. A toJSON method does not serialize: it returns the +// value represented by the name/value pair that should be serialized, +// or undefined if nothing should be serialized. The toJSON method +// will be passed the key associated with the value, and this will be +// bound to the value. + +// For example, this would serialize Dates as ISO strings. + +// Date.prototype.toJSON = function (key) { +// function f(n) { +// // Format integers to have at least two digits. +// return (n < 10) +// ? "0" + n +// : n; +// } +// return this.getUTCFullYear() + "-" + +// f(this.getUTCMonth() + 1) + "-" + +// f(this.getUTCDate()) + "T" + +// f(this.getUTCHours()) + ":" + +// f(this.getUTCMinutes()) + ":" + +// f(this.getUTCSeconds()) + "Z"; +// }; + +// You can provide an optional replacer method. It will be passed the +// key and value of each member, with this bound to the containing +// object. The value that is returned from your method will be +// serialized. If your method returns undefined, then the member will +// be excluded from the serialization. + +// If the replacer parameter is an array of strings, then it will be +// used to select the members to be serialized. It filters the results +// such that only members with keys listed in the replacer array are +// stringified. + +// Values that do not have JSON representations, such as undefined or +// functions, will not be serialized. Such values in objects will be +// dropped; in arrays they will be replaced with null. You can use +// a replacer function to replace those with JSON values. + +// JSON.stringify(undefined) returns undefined. + +// The optional space parameter produces a stringification of the +// value that is filled with line breaks and indentation to make it +// easier to read. + +// If the space parameter is a non-empty string, then that string will +// be used for indentation. If the space parameter is a number, then +// the indentation will be that many spaces. + +// Example: + +// text = JSON.stringify(["e", {pluribus: "unum"}]); +// // text is '["e",{"pluribus":"unum"}]' + +// text = JSON.stringify(["e", {pluribus: "unum"}], null, "\t"); +// // text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]' + +// text = JSON.stringify([new Date()], function (key, value) { +// return this[key] instanceof Date +// ? "Date(" + this[key] + ")" +// : value; +// }); +// // text is '["Date(---current time---)"]' + +// JSON.parse(text, reviver) +// This method parses a JSON text to produce an object or array. +// It can throw a SyntaxError exception. + +// The optional reviver parameter is a function that can filter and +// transform the results. It receives each of the keys and values, +// and its return value is used instead of the original value. +// If it returns what it received, then the structure is not modified. +// If it returns undefined then the member is deleted. + +// Example: + +// // Parse the text. Values that look like ISO date strings will +// // be converted to Date objects. + +// myData = JSON.parse(text, function (key, value) { +// var a; +// if (typeof value === "string") { +// a = +// /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value); +// if (a) { +// return new Date(Date.UTC( +// +a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6] +// )); +// } +// return value; +// } +// }); + +// myData = JSON.parse( +// "[\"Date(09/09/2001)\"]", +// function (key, value) { +// var d; +// if ( +// typeof value === "string" +// && value.slice(0, 5) === "Date(" +// && value.slice(-1) === ")" +// ) { +// d = new Date(value.slice(5, -1)); +// if (d) { +// return d; +// } +// } +// return value; +// } +// ); + +// This is a reference implementation. You are free to copy, modify, or +// redistribute. + +/*jslint + eval, for, this */ /*property @@ -166,42 +154,50 @@ // Create a JSON object only if one does not already exist. We create the // methods in a closure to avoid creating global variables. -if (typeof JSON !== 'object') { +if (typeof JSON !== "object") { JSON = {}; } (function () { - 'use strict'; - - var rx_one = /^[\],:{}\s]*$/, - rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, - rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, - rx_four = /(?:^|:|,)(?:\s*\[)+/g, - rx_escapable = /[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, - rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + "use strict"; + + var rx_one = /^[\],:{}\s]*$/; + var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; + var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; + var rx_four = /(?:^|:|,)(?:\s*\[)+/g; + var rx_escapable = /[\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; + var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; function f(n) { // Format integers to have at least two digits. - return n < 10 - ? '0' + n + return (n < 10) + ? "0" + n : n; } - + function this_value() { return this.valueOf(); } - if (typeof Date.prototype.toJSON !== 'function') { + if (typeof Date.prototype.toJSON !== "function") { Date.prototype.toJSON = function () { return isFinite(this.valueOf()) - ? this.getUTCFullYear() + '-' + - f(this.getUTCMonth() + 1) + '-' + - f(this.getUTCDate()) + 'T' + - f(this.getUTCHours()) + ':' + - f(this.getUTCMinutes()) + ':' + - f(this.getUTCSeconds()) + 'Z' + ? ( + this.getUTCFullYear() + + "-" + + f(this.getUTCMonth() + 1) + + "-" + + f(this.getUTCDate()) + + "T" + + f(this.getUTCHours()) + + ":" + + f(this.getUTCMinutes()) + + ":" + + f(this.getUTCSeconds()) + + "Z" + ) : null; }; @@ -210,10 +206,10 @@ if (typeof JSON !== 'object') { String.prototype.toJSON = this_value; } - var gap, - indent, - meta, - rep; + var gap; + var indent; + var meta; + var rep; function quote(string) { @@ -224,14 +220,14 @@ if (typeof JSON !== 'object') { // sequences. rx_escapable.lastIndex = 0; - return rx_escapable.test(string) - ? '"' + string.replace(rx_escapable, function (a) { + return rx_escapable.test(string) + ? "\"" + string.replace(rx_escapable, function (a) { var c = meta[a]; - return typeof c === 'string' + return typeof c === "string" ? c - : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }) + '"' - : '"' + string + '"'; + : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); + }) + "\"" + : "\"" + string + "\""; } @@ -239,61 +235,64 @@ if (typeof JSON !== 'object') { // Produce a string from holder[key]. - var i, // The loop counter. - k, // The member key. - v, // The member value. - length, - mind = gap, - partial, - value = holder[key]; + var i; // The loop counter. + var k; // The member key. + var v; // The member value. + var length; + var mind = gap; + var partial; + var value = holder[key]; // If the value has a toJSON method, call it to obtain a replacement value. - if (value && typeof value === 'object' && - typeof value.toJSON === 'function') { + if ( + value + && typeof value === "object" + && typeof value.toJSON === "function" + ) { value = value.toJSON(key); } // If we were called with a replacer function, then call the replacer to // obtain a replacement value. - if (typeof rep === 'function') { + if (typeof rep === "function") { value = rep.call(holder, key, value); } // What happens next depends on the value's type. switch (typeof value) { - case 'string': + case "string": return quote(value); - case 'number': + case "number": // JSON numbers must be finite. Encode non-finite numbers as null. - return isFinite(value) - ? String(value) - : 'null'; + return (isFinite(value)) + ? String(value) + : "null"; - case 'boolean': - case 'null': + case "boolean": + case "null": // If the value is a boolean or null, convert it to a string. Note: -// typeof null does not produce 'null'. The case is included here in +// typeof null does not produce "null". The case is included here in // the remote chance that this gets fixed someday. return String(value); -// If the type is 'object', we might be dealing with an object or an array or +// If the type is "object", we might be dealing with an object or an array or // null. - case 'object': + case "object": -// Due to a specification blunder in ECMAScript, typeof null is 'object', +// Due to a specification blunder in ECMAScript, typeof null is "object", // so watch out for that case. if (!value) { - return 'null'; + return "null"; } // Make an array to hold the partial results of stringifying this object value. @@ -303,41 +302,48 @@ if (typeof JSON !== 'object') { // Is the value an array? - if (Object.prototype.toString.apply(value) === '[object Array]') { + if (Object.prototype.toString.apply(value) === "[object Array]") { // The value is an array. Stringify every element. Use null as a placeholder // for non-JSON values. length = value.length; for (i = 0; i < length; i += 1) { - partial[i] = str(i, value) || 'null'; + partial[i] = str(i, value) || "null"; } // Join all of the elements together, separated with commas, and wrap them in // brackets. v = partial.length === 0 - ? '[]' + ? "[]" : gap - ? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' - : '[' + partial.join(',') + ']'; + ? ( + "[\n" + + gap + + partial.join(",\n" + gap) + + "\n" + + mind + + "]" + ) + : "[" + partial.join(",") + "]"; gap = mind; return v; } // If the replacer is an array, use it to select the members to be stringified. - if (rep && typeof rep === 'object') { + if (rep && typeof rep === "object") { length = rep.length; for (i = 0; i < length; i += 1) { - if (typeof rep[i] === 'string') { + if (typeof rep[i] === "string") { k = rep[i]; v = str(k, value); if (v) { partial.push(quote(k) + ( - gap - ? ': ' - : ':' + (gap) + ? ": " + : ":" ) + v); } } @@ -351,9 +357,9 @@ if (typeof JSON !== 'object') { v = str(k, value); if (v) { partial.push(quote(k) + ( - gap - ? ': ' - : ':' + (gap) + ? ": " + : ":" ) + v); } } @@ -364,10 +370,10 @@ if (typeof JSON !== 'object') { // and wrap them in braces. v = partial.length === 0 - ? '{}' + ? "{}" : gap - ? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' - : '{' + partial.join(',') + '}'; + ? "{\n" + gap + partial.join(",\n" + gap) + "\n" + mind + "}" + : "{" + partial.join(",") + "}"; gap = mind; return v; } @@ -375,15 +381,15 @@ if (typeof JSON !== 'object') { // If the JSON object does not yet have a stringify method, give it one. - if (typeof JSON.stringify !== 'function') { + if (typeof JSON.stringify !== "function") { meta = { // table of character substitutions - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '"': '\\"', - '\\': '\\\\' + "\b": "\\b", + "\t": "\\t", + "\n": "\\n", + "\f": "\\f", + "\r": "\\r", + "\"": "\\\"", + "\\": "\\\\" }; JSON.stringify = function (value, replacer, space) { @@ -394,20 +400,20 @@ if (typeof JSON !== 'object') { // produce text that is more easily readable. var i; - gap = ''; - indent = ''; + gap = ""; + indent = ""; // If the space parameter is a number, make an indent string containing that // many spaces. - if (typeof space === 'number') { + if (typeof space === "number") { for (i = 0; i < space; i += 1) { - indent += ' '; + indent += " "; } // If the space parameter is a string, it will be used as the indent string. - } else if (typeof space === 'string') { + } else if (typeof space === "string") { indent = space; } @@ -415,23 +421,24 @@ if (typeof JSON !== 'object') { // Otherwise, throw an error. rep = replacer; - if (replacer && typeof replacer !== 'function' && - (typeof replacer !== 'object' || - typeof replacer.length !== 'number')) { - throw new Error('JSON.stringify'); + if (replacer && typeof replacer !== "function" && ( + typeof replacer !== "object" + || typeof replacer.length !== "number" + )) { + throw new Error("JSON.stringify"); } -// Make a fake root object containing our value under the key of ''. +// Make a fake root object containing our value under the key of "". // Return the result of stringifying the value. - return str('', {'': value}); + return str("", {"": value}); }; } // If the JSON object does not yet have a parse method, give it one. - if (typeof JSON.parse !== 'function') { + if (typeof JSON.parse !== "function") { JSON.parse = function (text, reviver) { // The parse method takes a text and an optional reviver function, and returns @@ -444,8 +451,10 @@ if (typeof JSON !== 'object') { // The walk method is used to recursively walk the resulting structure so // that modifications can be made. - var k, v, value = holder[key]; - if (value && typeof value === 'object') { + var k; + var v; + var value = holder[key]; + if (value && typeof value === "object") { for (k in value) { if (Object.prototype.hasOwnProperty.call(value, k)) { v = walk(value, k); @@ -469,51 +478,53 @@ if (typeof JSON !== 'object') { rx_dangerous.lastIndex = 0; if (rx_dangerous.test(text)) { text = text.replace(rx_dangerous, function (a) { - return '\\u' + - ('0000' + a.charCodeAt(0).toString(16)).slice(-4); + return ( + "\\u" + + ("0000" + a.charCodeAt(0).toString(16)).slice(-4) + ); }); } // In the second stage, we run the text against regular expressions that look -// for non-JSON patterns. We are especially concerned with '()' and 'new' -// because they can cause invocation, and '=' because it can cause mutation. +// for non-JSON patterns. We are especially concerned with "()" and "new" +// because they can cause invocation, and "=" because it can cause mutation. // But just to be safe, we want to reject all unexpected forms. // We split the second stage into 4 regexp operations in order to work around // crippling inefficiencies in IE's and Safari's regexp engines. First we -// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we -// replace all simple value tokens with ']' characters. Third, we delete all +// replace the JSON backslash pairs with "@" (a non-JSON character). Second, we +// replace all simple value tokens with "]" characters. Third, we delete all // open brackets that follow a colon or comma or that begin the text. Finally, -// we look to see that the remaining characters are only whitespace or ']' or -// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval. +// we look to see that the remaining characters are only whitespace or "]" or +// "," or ":" or "{" or "}". If that is so, then the text is safe for eval. if ( rx_one.test( text - .replace(rx_two, '@') - .replace(rx_three, ']') - .replace(rx_four, '') + .replace(rx_two, "@") + .replace(rx_three, "]") + .replace(rx_four, "") ) ) { // In the third stage we use the eval function to compile the text into a -// JavaScript structure. The '{' operator is subject to a syntactic ambiguity +// JavaScript structure. The "{" operator is subject to a syntactic ambiguity // in JavaScript: it can begin a block or an object literal. We wrap the text // in parens to eliminate the ambiguity. - j = eval('(' + text + ')'); + j = eval("(" + text + ")"); // In the optional fourth stage, we recursively walk the new structure, passing // each name/value pair to a reviver function for possible transformation. - return typeof reviver === 'function' - ? walk({'': j}, '') + return (typeof reviver === "function") + ? walk({"": j}, "") : j; } // If the text is not JSON parseable, then a SyntaxError is thrown. - throw new SyntaxError('JSON.parse'); + throw new SyntaxError("JSON.parse"); }; } }()); diff --git a/src/MsieJavaScriptEngine/Resources/json2.min.js b/src/MsieJavaScriptEngine/Resources/json2.min.js deleted file mode 100644 index bd86fe2..0000000 --- a/src/MsieJavaScriptEngine/Resources/json2.min.js +++ /dev/null @@ -1 +0,0 @@ -typeof JSON!="object"&&(JSON={}),function(){"use strict";function i(n){return n<10?"0"+n:n}function o(){return this.valueOf()}function s(n){return f.lastIndex=0,f.test(n)?'"'+n.replace(f,function(n){var t=h[n];return typeof t=="string"?t:"\\u"+("0000"+n.charCodeAt(0).toString(16)).slice(-4)})+'"':'"'+n+'"'}function u(i,f){var o,l,h,a,v=n,c,e=f[i];e&&typeof e=="object"&&typeof e.toJSON=="function"&&(e=e.toJSON(i));typeof t=="function"&&(e=t.call(f,i,e));switch(typeof e){case"string":return s(e);case"number":return isFinite(e)?String(e):"null";case"boolean":case"null":return String(e);case"object":if(!e)return"null";if(n+=r,c=[],Object.prototype.toString.apply(e)==="[object Array]"){for(a=e.length,o=0;o + /// 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 Lock _taskQueueSynchronizer = new Lock(); + + /// + /// 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 is null) + { + _taskQueue.Clear(); + return; + } + } + } + + if (task is not 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 is not null) + { +#if NET45_OR_GREATER || NETSTANDARD || NET10_0_OR_GREATER + 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 is 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 is 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 is not null) + { + _thread.Join(); + _thread = null; + } + + if (_waitHandle is not 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 is not 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/MsieJavaScriptEngine/Undefined.cs b/src/MsieJavaScriptEngine/Undefined.cs index 87fdfd7..a7097fc 100644 --- a/src/MsieJavaScriptEngine/Undefined.cs +++ b/src/MsieJavaScriptEngine/Undefined.cs @@ -1,12 +1,12 @@ namespace MsieJavaScriptEngine { /// - /// 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(); diff --git a/src/MsieJavaScriptEngine/Utilities/EnvironmentShortcuts.cs b/src/MsieJavaScriptEngine/Utilities/EnvironmentShortcuts.cs new file mode 100644 index 0000000..d637ce2 --- /dev/null +++ b/src/MsieJavaScriptEngine/Utilities/EnvironmentShortcuts.cs @@ -0,0 +1,15 @@ +using System; + +namespace MsieJavaScriptEngine.Utilities +{ + /// + /// Shortcuts for accessing to values that depend on the current environment and platform + /// + internal static class EnvironmentShortcuts + { + /// + /// Gets a array of the newline characters + /// + internal static readonly char[] NewLineChars = Environment.NewLine == "\r\n" ? ['\r', '\n'] : ['\n', '\r']; + } +} \ No newline at end of file diff --git a/src/MsieJavaScriptEngine/InterlockedStatedFlag.cs b/src/MsieJavaScriptEngine/Utilities/InterlockedStatedFlag.cs similarity index 75% rename from src/MsieJavaScriptEngine/InterlockedStatedFlag.cs rename to src/MsieJavaScriptEngine/Utilities/InterlockedStatedFlag.cs index 8cdf01b..8875214 100644 --- a/src/MsieJavaScriptEngine/InterlockedStatedFlag.cs +++ b/src/MsieJavaScriptEngine/Utilities/InterlockedStatedFlag.cs @@ -1,7 +1,7 @@ -namespace MsieJavaScriptEngine -{ - using System.Threading; +using System.Threading; +namespace MsieJavaScriptEngine.Utilities +{ internal struct InterlockedStatedFlag { private int _counter; diff --git a/src/MsieJavaScriptEngine/StatedFlag.cs b/src/MsieJavaScriptEngine/Utilities/StatedFlag.cs similarity index 83% rename from src/MsieJavaScriptEngine/StatedFlag.cs rename to src/MsieJavaScriptEngine/Utilities/StatedFlag.cs index a90a429..a7c45c2 100644 --- a/src/MsieJavaScriptEngine/StatedFlag.cs +++ b/src/MsieJavaScriptEngine/Utilities/StatedFlag.cs @@ -1,4 +1,4 @@ -namespace MsieJavaScriptEngine +namespace MsieJavaScriptEngine.Utilities { internal struct StatedFlag { diff --git a/src/MsieJavaScriptEngine/Utilities/StringBuilderExtensions.cs b/src/MsieJavaScriptEngine/Utilities/StringBuilderExtensions.cs deleted file mode 100644 index 2944b91..0000000 --- a/src/MsieJavaScriptEngine/Utilities/StringBuilderExtensions.cs +++ /dev/null @@ -1,47 +0,0 @@ -namespace MsieJavaScriptEngine.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/MsieJavaScriptEngine/Utilities/TypeConverter.cs b/src/MsieJavaScriptEngine/Utilities/TypeConverter.cs index 4c85817..115a8fd 100644 --- a/src/MsieJavaScriptEngine/Utilities/TypeConverter.cs +++ b/src/MsieJavaScriptEngine/Utilities/TypeConverter.cs @@ -1,13 +1,20 @@ -namespace MsieJavaScriptEngine.Utilities +using System; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +#if NET45_OR_GREATER || NETSTANDARD || NET10_0_OR_GREATER +using System.Reflection; +#endif +using OriginalTypeConverter = System.ComponentModel.TypeConverter; + +using MsieJavaScriptEngine.Extensions; +#if NETSTANDARD1_3 +using MsieJavaScriptEngine.Polyfills.System.Reflection; +#endif +using MsieJavaScriptEngine.Resources; + +namespace MsieJavaScriptEngine.Utilities { - using System; - using System.ComponentModel; - using System.Globalization; - using System.Linq; - using OriginalTypeConverter = System.ComponentModel.TypeConverter; - - using Resources; - /// /// Type converter /// @@ -16,14 +23,14 @@ public static class TypeConverter /// /// List of primitive type codes /// - private static readonly TypeCode[] _primitiveTypeCodes = new[] - { + private static readonly TypeCode[] _primitiveTypeCodes = + [ TypeCode.Boolean, TypeCode.SByte, TypeCode.Byte, TypeCode.Int16, TypeCode.UInt16, TypeCode.Int32, TypeCode.UInt32, TypeCode.Int64, TypeCode.UInt64, TypeCode.Single, TypeCode.Double, TypeCode.Decimal, TypeCode.Char, TypeCode.String - }; + ]; /// /// Converts the specified value to the specified type @@ -57,7 +64,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; @@ -75,7 +82,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); @@ -87,10 +94,10 @@ public static bool TryConvertToType(object value, Type targetType, out object co /// 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) internal static bool IsPrimitiveType(Type type) { - TypeCode typeCode = Type.GetTypeCode(type); + TypeCode typeCode = type.GetTypeCode(); bool result = IsPrimitiveType(typeCode); return result; @@ -100,7 +107,7 @@ internal static bool IsPrimitiveType(Type type) /// Checks whether .NET type is primitive /// /// .NET type code - /// Result of check (true - is primitive; false - is not primitive) + /// Result of check (true - is primitive; false - is not primitive) internal static bool IsPrimitiveType(TypeCode typeCode) { bool result = _primitiveTypeCodes.Contains(typeCode); @@ -111,13 +118,13 @@ internal static bool IsPrimitiveType(TypeCode typeCode) private static bool ConvertObjectToType(object obj, Type type, bool throwOnError, out object convertedObject) { - if (obj == null) + if (obj is null) { if (IsNonNullableValueType(type)) { if (throwOnError) { - throw new InvalidOperationException(Strings.Common_ValueTypeCannotBeNull); + throw new InvalidOperationException(CommonStrings.Common_ValueTypeCannotBeNull); } convertedObject = null; @@ -128,7 +135,7 @@ private static bool ConvertObjectToType(object obj, Type type, bool throwOnError return true; } - if (type != null && obj.GetType() != type) + if (type is not null && obj.GetType() != type) { return InnerConvertObjectToType(obj, type, throwOnError, out convertedObject); } @@ -140,9 +147,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 { @@ -165,7 +173,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; @@ -182,7 +190,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; @@ -191,7 +203,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(CommonStrings.Common_CannotConvertObjectToType, originalType, type) ); } @@ -201,14 +213,24 @@ private static bool InnerConvertObjectToType(object obj, Type type, bool throwOn private static bool IsNonNullableValueType(Type type) { - if (type == null || !type.IsValueType) + if (type is 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/MsieJavaScriptEngine/Utilities/UniqueDocumentNameManager.cs b/src/MsieJavaScriptEngine/Utilities/UniqueDocumentNameManager.cs new file mode 100644 index 0000000..15b3c43 --- /dev/null +++ b/src/MsieJavaScriptEngine/Utilities/UniqueDocumentNameManager.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using System.IO; + +using MsieJavaScriptEngine.Resources; + +namespace MsieJavaScriptEngine.Utilities +{ + /// + /// Unique document name manager + /// + internal 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 Lock _storageSynchronizer = new Lock(); + + + /// + /// Constructs an instance of the unique document name manager + /// + /// Default document name + public UniqueDocumentNameManager(string defaultName) + { + if (defaultName is null) + { + throw new ArgumentNullException( + nameof(defaultName), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(defaultName)) + ); + } + + if (string.IsNullOrWhiteSpace(defaultName)) + { + throw new ArgumentException( + string.Format(CommonStrings.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/MsieJavaScriptEngine/Utilities/Utils.cs b/src/MsieJavaScriptEngine/Utilities/Utils.cs index 3184fab..09ddd99 100644 --- a/src/MsieJavaScriptEngine/Utilities/Utils.cs +++ b/src/MsieJavaScriptEngine/Utilities/Utils.cs @@ -1,41 +1,70 @@ -namespace MsieJavaScriptEngine.Utilities -{ - using System; - using System.IO; - using System.Reflection; - using System.Text; +using System; +using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Text; - using Resources; +using MsieJavaScriptEngine.Resources; +namespace MsieJavaScriptEngine.Utilities +{ internal static class Utils { /// - /// Gets a content of the embedded resource as string + /// Determines whether the current process is a 64-bit process /// - /// Resource name - /// Type from assembly that containing an embedded resource - /// Сontent of the embedded resource as string - public static string GetResourceAsString(string resourceName, Type type) + /// true if the process is 64-bit; otherwise, false + [MethodImpl((MethodImplOptions)256 /* AggressiveInlining */)] + public static bool Is64BitProcess() { - Assembly assembly = type.Assembly; +#if NETSTANDARD1_3 + bool is64Bit = IntPtr.Size == 8; +#else + bool is64Bit = Environment.Is64BitProcess; +#endif - return GetResourceAsString(resourceName, assembly); + return is64Bit; } /// /// 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 is null) + { + throw new ArgumentNullException( + nameof(resourceName), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(resourceName)) + ); + } + + if (assembly is null) + { + throw new ArgumentNullException( + nameof(assembly), + string.Format(CommonStrings.Common_ArgumentIsNull, nameof(assembly)) + ); + } + + if (string.IsNullOrWhiteSpace(resourceName)) + { + throw new ArgumentException( + string.Format(CommonStrings.Common_ArgumentIsEmpty, nameof(resourceName)), + nameof(resourceName) + ); + } + using (Stream stream = assembly.GetManifestResourceStream(resourceName)) { - if (stream == null) + if (stream is null) { throw new NullReferenceException( - string.Format(Strings.Resources_ResourceIsNull, resourceName)); + string.Format(CommonStrings.Common_ResourceIsNull, resourceName) + ); } using (var reader = new StreamReader(stream)) @@ -46,7 +75,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,14 +85,17 @@ 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(CommonStrings.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; diff --git a/src/MsieJavaScriptEngine/package.json b/src/MsieJavaScriptEngine/package.json new file mode 100644 index 0000000..81299ea --- /dev/null +++ b/src/MsieJavaScriptEngine/package.json @@ -0,0 +1,12 @@ +{ + "name": "MsieJavaScriptEngine", + "version": "3.3.1", + "devDependencies": { + "uglify-js": "3.16.1" + }, + "scripts": { + "minify-es5-js": "uglifyjs ./Resources/ES5.js --output ./Resources/ES5.min.js --config-file ./.uglifyjsrc", + "minify-json2-js": "uglifyjs ./Resources/json2.js --output ./Resources/json2.min.js --config-file ./.uglifyjsrc", + "minify-js": "npm run -s minify-es5-js && npm run -s minify-json2-js" + } +} diff --git a/src/MsieJavaScriptEngine/readme.txt b/src/MsieJavaScriptEngine/readme.txt new file mode 100644 index 0000000..8bc9a4d --- /dev/null +++ b/src/MsieJavaScriptEngine/readme.txt @@ -0,0 +1,29 @@ + + + -------------------------------------------------------------------------------- + README file for MSIE JavaScript Engine for .NET v3.3.1 + + -------------------------------------------------------------------------------- + + Copyright (c) 2012-2026 Andrey Taritsyn - http://www.taritsyn.ru + + + =========== + DESCRIPTION + =========== + This library is a .NET wrapper for working with the JavaScript engines of + Internet Explorer and Edge Legacy (JsRT versions of Chakra, ActiveScript version + of Chakra and Classic JavaScript Engine). Project was based on the code of + SassAndCoffee.JavaScript (https://github.com/anaisbetts/SassAndCoffee), + Chakra Sample Hosts (https://github.com/panopticoncentral/chakra-host) and + jsrt-dotnet (https://github.com/robpaveza/jsrt-dotnet). + + ============= + RELEASE NOTES + ============= + Was made refactoring. + + ============ + PROJECT SITE + ============ + https://github.com/Taritsyn/MsieJavaScriptEngine \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Benchmarks/Assert.cs b/test/MsieJavaScriptEngine.Benchmarks/Assert.cs new file mode 100644 index 0000000..e794112 --- /dev/null +++ b/test/MsieJavaScriptEngine.Benchmarks/Assert.cs @@ -0,0 +1,24 @@ +using System; +using System.Text; + +namespace MsieJavaScriptEngine.Benchmarks +{ + internal static class Assert + { + public static void Equal(string expected, string actual) + { + if (actual != expected) + { + 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); + } + } + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Benchmarks/HostObjectsEmbeddingBenchmark.cs b/test/MsieJavaScriptEngine.Benchmarks/HostObjectsEmbeddingBenchmark.cs new file mode 100644 index 0000000..700a6bf --- /dev/null +++ b/test/MsieJavaScriptEngine.Benchmarks/HostObjectsEmbeddingBenchmark.cs @@ -0,0 +1,138 @@ +using System; +using System.Text; + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Order; + +using MsieJavaScriptEngine.Benchmarks.Interop.ObjectsEmbedding; + +namespace MsieJavaScriptEngine.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));"; +#if NET462 + const string targetOutput = "RmFsc2V8MjkxNHwwLjg3Mjg1OTEwNzM4ODQyNHxBU0RG0KTQq9CS0JA="; +#else + const string targetOutput = "RmFsc2V8MjkxNHwwLjg3Mjg1OTEwNzM4ODQyMzV8QVNERtCk0KvQktCQ"; +#endif + 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); + } +#if NET462 + + [Benchmark] + public void Classic() + { + Func createJsEngine = () => new MsieJsEngine(new JsEngineSettings{ + EngineMode = JsEngineMode.Classic + }); + EmbedAndUseHostObjects(createJsEngine); + } + + [Benchmark] + public void ChakraActiveScript() + { + Func createJsEngine = () => new MsieJsEngine(new JsEngineSettings + { + EngineMode = JsEngineMode.ChakraActiveScript + }); + EmbedAndUseHostObjects(createJsEngine); + } +#endif + + [Benchmark] + public void ChakraIeJsRt() + { + Func createJsEngine = () => new MsieJsEngine(new JsEngineSettings + { + EngineMode = JsEngineMode.ChakraIeJsRt + }); + EmbedAndUseHostObjects(createJsEngine); + } + + [Benchmark] + public void ChakraEdgeJsRt() + { + Func createJsEngine = () => new MsieJsEngine(new JsEngineSettings + { + EngineMode = JsEngineMode.ChakraEdgeJsRt + }); + EmbedAndUseHostObjects(createJsEngine); + } + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Benchmarks/HostTypesEmbeddingBenchmark.cs b/test/MsieJavaScriptEngine.Benchmarks/HostTypesEmbeddingBenchmark.cs new file mode 100644 index 0000000..4bd965c --- /dev/null +++ b/test/MsieJavaScriptEngine.Benchmarks/HostTypesEmbeddingBenchmark.cs @@ -0,0 +1,118 @@ +using System; +using System.Drawing; + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Order; + +using MsieJavaScriptEngine.Benchmarks.Interop.TypesEmbedding; + +namespace MsieJavaScriptEngine.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));"; +#if NET462 + const string targetOutput = "RmFsc2V8MjkyMHwwLjg3Mjg1OTEwNzM4ODQyNHxBU0RG0KTQq9CS0JA="; +#else + const string targetOutput = "RmFsc2V8MjkyMHwwLjg3Mjg1OTEwNzM4ODQyMzV8QVNERtCk0KvQktCQ"; +#endif + + // 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); + } +#if NET462 + + [Benchmark] + public void Classic() + { + Func createJsEngine = () => new MsieJsEngine(new JsEngineSettings{ + EngineMode = JsEngineMode.Classic + }); + EmbedAndUseHostTypes(createJsEngine); + } + + [Benchmark] + public void ChakraActiveScript() + { + Func createJsEngine = () => new MsieJsEngine(new JsEngineSettings + { + EngineMode = JsEngineMode.ChakraActiveScript + }); + EmbedAndUseHostTypes(createJsEngine); + } +#endif + + [Benchmark] + public void ChakraIeJsRt() + { + Func createJsEngine = () => new MsieJsEngine(new JsEngineSettings + { + EngineMode = JsEngineMode.ChakraIeJsRt + }); + EmbedAndUseHostTypes(createJsEngine); + } + + [Benchmark] + public void ChakraEdgeJsRt() + { + Func createJsEngine = () => new MsieJsEngine(new JsEngineSettings + { + EngineMode = JsEngineMode.ChakraEdgeJsRt + }); + EmbedAndUseHostTypes(createJsEngine); + } + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Benchmarks/Interop/ObjectsEmbedding/SomeClass.cs b/test/MsieJavaScriptEngine.Benchmarks/Interop/ObjectsEmbedding/SomeClass.cs new file mode 100644 index 0000000..14a20fd --- /dev/null +++ b/test/MsieJavaScriptEngine.Benchmarks/Interop/ObjectsEmbedding/SomeClass.cs @@ -0,0 +1,34 @@ +using System; +using System.Drawing; +using System.Linq; +using System.Text; + +namespace MsieJavaScriptEngine.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.Round(arg3) + + Encoding.UTF8.GetBytes(arg4).Sum(x => x); + ; + + return result; + } + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Benchmarks/Interop/ObjectsEmbedding/SomeClassBase.cs b/test/MsieJavaScriptEngine.Benchmarks/Interop/ObjectsEmbedding/SomeClassBase.cs new file mode 100644 index 0000000..4a97f7d --- /dev/null +++ b/test/MsieJavaScriptEngine.Benchmarks/Interop/ObjectsEmbedding/SomeClassBase.cs @@ -0,0 +1,15 @@ +namespace MsieJavaScriptEngine.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/MsieJavaScriptEngine.Benchmarks/Interop/ObjectsEmbedding/SomeOtherClass.cs b/test/MsieJavaScriptEngine.Benchmarks/Interop/ObjectsEmbedding/SomeOtherClass.cs new file mode 100644 index 0000000..31f7c2d --- /dev/null +++ b/test/MsieJavaScriptEngine.Benchmarks/Interop/ObjectsEmbedding/SomeOtherClass.cs @@ -0,0 +1,21 @@ +using System; +using System.Globalization; +using System.Text; + +namespace MsieJavaScriptEngine.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) + "|" + + arg3.ToString(CultureInfo.InvariantCulture) + "|" + + arg4 + ; + string result = Convert.ToBase64String(Encoding.UTF8.GetBytes(rawResult)); + + return result; + } + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Benchmarks/Interop/TypesEmbedding/SomeClass.cs b/test/MsieJavaScriptEngine.Benchmarks/Interop/TypesEmbedding/SomeClass.cs new file mode 100644 index 0000000..e0ac877 --- /dev/null +++ b/test/MsieJavaScriptEngine.Benchmarks/Interop/TypesEmbedding/SomeClass.cs @@ -0,0 +1,34 @@ +using System; +using System.Drawing; +using System.Linq; +using System.Text; + +namespace MsieJavaScriptEngine.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.Round(arg3) + + Encoding.UTF8.GetBytes(arg4).Sum(x => x); + ; + + return result; + } + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Benchmarks/Interop/TypesEmbedding/SomeOtherClass.cs b/test/MsieJavaScriptEngine.Benchmarks/Interop/TypesEmbedding/SomeOtherClass.cs new file mode 100644 index 0000000..0f73fce --- /dev/null +++ b/test/MsieJavaScriptEngine.Benchmarks/Interop/TypesEmbedding/SomeOtherClass.cs @@ -0,0 +1,47 @@ +using System; +using System.Globalization; +using System.Text; + +namespace MsieJavaScriptEngine.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) + "|" + + arg3.ToString(CultureInfo.InvariantCulture) + "|" + + arg4 + ; + string result = Convert.ToBase64String(Encoding.UTF8.GetBytes(rawResult)); + + return result; + } + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Benchmarks/JsExecutionBenchmark.cs b/test/MsieJavaScriptEngine.Benchmarks/JsExecutionBenchmark.cs new file mode 100644 index 0000000..633680b --- /dev/null +++ b/test/MsieJavaScriptEngine.Benchmarks/JsExecutionBenchmark.cs @@ -0,0 +1,231 @@ +using System; +using System.IO; +using System.Reflection; + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Order; + +namespace MsieJavaScriptEngine.Benchmarks +{ + [MemoryDiagnoser] + [Orderer(SummaryOrderPolicy.Method, MethodOrderPolicy.Declared)] + public class JsExecutionBenchmark + { + /// + /// 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 JsExecutionBenchmark() + { + PopulateTestData(); + } + + + /// + /// Populates a test data + /// + public static void PopulateTestData() + { + Type type = typeof(JsExecutionBenchmark); + Assembly assembly = type.Assembly; + string resourceName = type.Namespace + ".Resources." + LibraryFileName; + + using (Stream stream = assembly.GetManifestResourceStream(resourceName)) + using (StreamReader reader = new StreamReader(stream)) + { + _libraryCode = reader.ReadToEnd(); + } + + _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]; + PrecompiledScript precompiledCode = null; + + // Act + using (var jsEngine = createJsEngine()) + { + if (withPrecompilation) + { + if (!jsEngine.SupportsScriptPrecompilation) + { + throw new NotSupportedException($"{jsEngine.Mode} mode 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]); + } + } +#if NET462 + + [Benchmark] + public void Classic() + { + Func createJsEngine = () => new MsieJsEngine(new JsEngineSettings{ + EngineMode = JsEngineMode.Classic + }); + TransliterateStrings(createJsEngine, false); + } + + [Benchmark] + public void ChakraActiveScript() + { + Func createJsEngine = () => new MsieJsEngine(new JsEngineSettings + { + EngineMode = JsEngineMode.ChakraActiveScript + }); + TransliterateStrings(createJsEngine, false); + } +#endif + + [Benchmark] + [Arguments(false)] + [Arguments(true)] + public void ChakraIeJsRt(bool withPrecompilation) + { + Func createJsEngine = () => new MsieJsEngine(new JsEngineSettings + { + EngineMode = JsEngineMode.ChakraIeJsRt + }); + TransliterateStrings(createJsEngine, withPrecompilation); + } + + [Benchmark] + [Arguments(false)] + [Arguments(true)] + public void ChakraEdgeJsRt(bool withPrecompilation) + { + Func createJsEngine = () => new MsieJsEngine(new JsEngineSettings + { + EngineMode = JsEngineMode.ChakraEdgeJsRt + }); + TransliterateStrings(createJsEngine, withPrecompilation); + } + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Benchmarks/MsieJavaScriptEngine.Benchmarks.csproj b/test/MsieJavaScriptEngine.Benchmarks/MsieJavaScriptEngine.Benchmarks.csproj new file mode 100644 index 0000000..8bdafcd --- /dev/null +++ b/test/MsieJavaScriptEngine.Benchmarks/MsieJavaScriptEngine.Benchmarks.csproj @@ -0,0 +1,27 @@ + + + + MSIE JavaScript Engine: Benchmarks + 3.3.1 + net462;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0 + Exe + true + true + false + true + false + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Benchmarks/Program.cs b/test/MsieJavaScriptEngine.Benchmarks/Program.cs new file mode 100644 index 0000000..7376ad7 --- /dev/null +++ b/test/MsieJavaScriptEngine.Benchmarks/Program.cs @@ -0,0 +1,14 @@ +using System.Reflection; + +using BenchmarkDotNet.Running; + +namespace MsieJavaScriptEngine.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/MsieJavaScriptEngine.Benchmarks/Resources/russian-translit.js b/test/MsieJavaScriptEngine.Benchmarks/Resources/russian-translit.js new file mode 100644 index 0000000..7e68866 --- /dev/null +++ b/test/MsieJavaScriptEngine.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/MsieJavaScriptEngine.Test.Auto/CommonTests.cs b/test/MsieJavaScriptEngine.Test.Auto/CommonTests.cs index 4b4a89b..12fff1c 100644 --- a/test/MsieJavaScriptEngine.Test.Auto/CommonTests.cs +++ b/test/MsieJavaScriptEngine.Test.Auto/CommonTests.cs @@ -1,20 +1,12 @@ -namespace MsieJavaScriptEngine.Test.Auto -{ - using MsieJavaScriptEngine; - using Common; +using NUnit.Framework; + +using MsieJavaScriptEngine.Test.Common; +namespace MsieJavaScriptEngine.Test.Auto +{ + [TestFixture] public class CommonTests : CommonTestsBase { - protected override MsieJsEngine CreateJsEngine() - { - var jsEngine = new MsieJsEngine(new JsEngineSettings - { - EngineMode = JsEngineMode.Auto, - UseEcmaScript5Polyfill = false, - UseJson2Library = false - }); - - return jsEngine; - } + protected override JsEngineMode EngineMode => JsEngineMode.Auto; } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Auto/Es5Tests.cs b/test/MsieJavaScriptEngine.Test.Auto/Es5Tests.cs index 3d47b65..4bdc032 100644 --- a/test/MsieJavaScriptEngine.Test.Auto/Es5Tests.cs +++ b/test/MsieJavaScriptEngine.Test.Auto/Es5Tests.cs @@ -1,23 +1,14 @@ -namespace MsieJavaScriptEngine.Test.Auto -{ - using NUnit.Framework; +using NUnit.Framework; - using MsieJavaScriptEngine; - using Common; +using MsieJavaScriptEngine.Test.Common; +namespace MsieJavaScriptEngine.Test.Auto +{ [TestFixture] public class Es5Tests : Es5TestsBase { - protected override MsieJsEngine CreateJsEngine() - { - var jsEngine = new MsieJsEngine(new JsEngineSettings - { - EngineMode = JsEngineMode.Auto, - UseEcmaScript5Polyfill = true, - UseJson2Library = true - }); - - return jsEngine; - } + protected override JsEngineMode EngineMode => JsEngineMode.Auto; + protected override bool UseEcmaScript5Polyfill => true; + protected override bool UseJson2Library => true; } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Auto/InteropTests.cs b/test/MsieJavaScriptEngine.Test.Auto/InteropTests.cs index 712c3fc..3034802 100644 --- a/test/MsieJavaScriptEngine.Test.Auto/InteropTests.cs +++ b/test/MsieJavaScriptEngine.Test.Auto/InteropTests.cs @@ -1,23 +1,52 @@ -namespace MsieJavaScriptEngine.Test.Auto -{ - using NUnit.Framework; +using NUnit.Framework; - using MsieJavaScriptEngine; - using Common; +using MsieJavaScriptEngine.Test.Common; +namespace MsieJavaScriptEngine.Test.Auto +{ [TestFixture] public class InteropTests : InteropTestsBase { - protected override MsieJsEngine CreateJsEngine() - { - var jsEngine = new MsieJsEngine(new JsEngineSettings - { - EngineMode = JsEngineMode.Auto, - UseEcmaScript5Polyfill = false, - UseJson2Library = false - }); - - return jsEngine; - } + protected override JsEngineMode EngineMode => JsEngineMode.Auto; + + + #region Embedding of objects + + #region Objects with methods + + public override void EmbeddingOfInstanceOfCustomValueTypeAndCallingOfItsGetTypeMethod() + { } + + public override void EmbeddingOfInstanceOfCustomReferenceTypeAndCallingOfItsGetTypeMethod() + { } + + #endregion + + #region Delegates + + public override void EmbeddingOfInstanceOfDelegateAndCheckingItsPrototype() + { } + + public override void EmbeddingOfInstanceOfDelegateAndGettingItsMethodProperty() + { } + + #endregion + + #endregion + + + #region Embedding of types + + #region Creating of instances + + public override void CreatingAnInstanceOfEmbeddedBuiltinExceptionAndGettingItsTargetSiteProperty() + { } + + public override void CreatingAnInstanceOfEmbeddedCustomExceptionAndCallingOfItsGetTypeMethod() + { } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Auto/MsieJavaScriptEngine.Test.Auto.csproj b/test/MsieJavaScriptEngine.Test.Auto/MsieJavaScriptEngine.Test.Auto.csproj index 5fb2f25..78a1dc8 100644 --- a/test/MsieJavaScriptEngine.Test.Auto/MsieJavaScriptEngine.Test.Auto.csproj +++ b/test/MsieJavaScriptEngine.Test.Auto/MsieJavaScriptEngine.Test.Auto.csproj @@ -1,76 +1,23 @@ - - - + + - Debug - AnyCPU - {A4874C43-2097-4FDB-882A-0D74E5508BBD} + MSIE JavaScript Engine: Tests for Auto Mode + 3.3.1 + net462;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0 Library - Properties - MsieJavaScriptEngine.Test.Auto - MsieJavaScriptEngine.Test.Auto - v4.0 - 512 - ..\..\ - true + true + false + true + true + false - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll - True - - - - - - - - - - - - - {d672bc49-c454-4975-bd25-a555b9bdd793} - MsieJavaScriptEngine - - - {a4085b9e-a5d3-4749-bd07-6d1eb7c23820} - MsieJavaScriptEngine.Test.Common - - + + + - + + + - - - - - 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/MsieJavaScriptEngine.Test.Auto/MultithreadingTests.cs b/test/MsieJavaScriptEngine.Test.Auto/MultithreadingTests.cs new file mode 100644 index 0000000..5077e7e --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Auto/MultithreadingTests.cs @@ -0,0 +1,12 @@ +using NUnit.Framework; + +using MsieJavaScriptEngine.Test.Common; + +namespace MsieJavaScriptEngine.Test.Auto +{ + [TestFixture] + public class MultithreadingTests : MultithreadingTestsBase + { + protected override JsEngineMode EngineMode => JsEngineMode.Auto; + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Auto/PrecompilationTests.cs b/test/MsieJavaScriptEngine.Test.Auto/PrecompilationTests.cs new file mode 100644 index 0000000..f846854 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Auto/PrecompilationTests.cs @@ -0,0 +1,12 @@ +using NUnit.Framework; + +using MsieJavaScriptEngine.Test.Common; + +namespace MsieJavaScriptEngine.Test.Auto +{ + [TestFixture] + public class PrecompilationTests : PrecompilationTestsBase + { + protected override JsEngineMode EngineMode => JsEngineMode.Auto; + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Auto/Properties/AssemblyInfo.cs b/test/MsieJavaScriptEngine.Test.Auto/Properties/AssemblyInfo.cs deleted file mode 100644 index f9cd7f2..0000000 --- a/test/MsieJavaScriptEngine.Test.Auto/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("MsieJavaScriptEngine.Test.Auto")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("MSIE JavaScript Engine: Tests for Auto Mode")] -[assembly: AssemblyCopyright("Copyright © 2012-2016 Andrey Taritsyn")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("e9baa5d3-59fe-4c0f-82ee-d415ffe53903")] - -[assembly: AssemblyVersion("1.7.0.0")] -[assembly: AssemblyFileVersion("1.7.0.0")] \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Auto/packages.config b/test/MsieJavaScriptEngine.Test.Auto/packages.config deleted file mode 100644 index 512ce05..0000000 --- a/test/MsieJavaScriptEngine.Test.Auto/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraActiveScript/CommonTests.cs b/test/MsieJavaScriptEngine.Test.ChakraActiveScript/CommonTests.cs index 37a4868..b33e52e 100644 --- a/test/MsieJavaScriptEngine.Test.ChakraActiveScript/CommonTests.cs +++ b/test/MsieJavaScriptEngine.Test.ChakraActiveScript/CommonTests.cs @@ -1,23 +1,362 @@ -namespace MsieJavaScriptEngine.Test.ChakraActiveScript -{ - using NUnit.Framework; +using System; + +using NUnit.Framework; - using MsieJavaScriptEngine; - using Common; +using MsieJavaScriptEngine.Test.Common; +namespace MsieJavaScriptEngine.Test.ChakraActiveScript +{ [TestFixture] public class CommonTests : CommonTestsBase { - protected override MsieJsEngine CreateJsEngine() + protected override JsEngineMode EngineMode => JsEngineMode.ChakraActiveScript; + + + #region Error handling + + #region Mapping of errors + + [Test] + 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.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Conditional compilation is turned off", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.AreEqual("variables.js", exception.DocumentName); + Assert.AreEqual(3, exception.LineNumber); + Assert.AreEqual(15, exception.ColumnNumber); + Assert.AreEqual("var @variable3 = 678;", exception.SourceFragment); + } + + [Test] + public void MappingCompilationErrorDuringEvaluationOfExpressionInDebugMode() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var @variable3 = 678; + +$variable1 + _variable2 - @variable3;"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine(enableDebugging: true)) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Conditional compilation is turned off", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.AreEqual("variables.js", exception.DocumentName); + Assert.AreEqual(3, exception.LineNumber); + Assert.AreEqual(15, exception.ColumnNumber); + Assert.AreEqual("var @variable3 = 678;", exception.SourceFragment); + } + + [Test] + 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.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("'variable2' is undefined", exception.Description); + Assert.AreEqual("ReferenceError", exception.Type); + Assert.AreEqual("variables.js", exception.DocumentName); + Assert.AreEqual(5, exception.LineNumber); + Assert.AreEqual(1, exception.ColumnNumber); + Assert.IsEmpty(exception.SourceFragment); + Assert.IsEmpty(exception.CallStack); + } + + [Test] + 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.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Syntax error", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.AreEqual("factorial.js", exception.DocumentName); + Assert.AreEqual(10, exception.LineNumber); + Assert.AreEqual(13, exception.ColumnNumber); + Assert.AreEqual("factorial(2%);", exception.SourceFragment); + } + + [Test] + public void MappingCompilationErrorDuringExecutionOfCodeInDebugMode() + { + // 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(enableDebugging: true)) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Syntax error", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.AreEqual("factorial.js", exception.DocumentName); + Assert.AreEqual(10, exception.LineNumber); + Assert.AreEqual(13, exception.ColumnNumber); + Assert.AreEqual("factorial(2%);", exception.SourceFragment); + } + + [Test] + 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.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("The value must be greater than or equal to zero.", exception.Description); + Assert.IsEmpty(exception.Type); + Assert.AreEqual("factorial.js", exception.DocumentName); + Assert.AreEqual(3, exception.LineNumber); + Assert.AreEqual(3, exception.ColumnNumber); + Assert.IsEmpty(exception.SourceFragment); + Assert.IsEmpty(exception.CallStack); + } + + #endregion + + #region Generation of error messages + + [Test] + public void GenerationOfCompilationErrorMessage() { - var jsEngine = new MsieJsEngine(new JsEngineSettings + // 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()) { - EngineMode = JsEngineMode.ChakraActiveScript, - UseEcmaScript5Polyfill = false, - UseJson2Library = false - }); + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } - return jsEngine; + Assert.NotNull(exception); + Assert.AreEqual(targetOutput, exception.Message); } + + [Test] + public void GenerationOfCompilationErrorMessageInDebugMode() + { + // 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(enableDebugging: true)) + { + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.AreEqual(targetOutput, exception.Message); + } + + [Test] + 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 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.AreEqual(targetOutput, exception.Message); + } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraActiveScript/Es5Tests.cs b/test/MsieJavaScriptEngine.Test.ChakraActiveScript/Es5Tests.cs index 5e36bbc..2075b11 100644 --- a/test/MsieJavaScriptEngine.Test.ChakraActiveScript/Es5Tests.cs +++ b/test/MsieJavaScriptEngine.Test.ChakraActiveScript/Es5Tests.cs @@ -1,23 +1,12 @@ -namespace MsieJavaScriptEngine.Test.ChakraActiveScript -{ - using NUnit.Framework; +using NUnit.Framework; - using MsieJavaScriptEngine; - using Common; +using MsieJavaScriptEngine.Test.Common; +namespace MsieJavaScriptEngine.Test.ChakraActiveScript +{ [TestFixture] public class Es5Tests : Es5TestsBase { - protected override MsieJsEngine CreateJsEngine() - { - var jsEngine = new MsieJsEngine(new JsEngineSettings - { - EngineMode = JsEngineMode.ChakraActiveScript, - UseEcmaScript5Polyfill = false, - UseJson2Library = false - }); - - return jsEngine; - } + protected override JsEngineMode EngineMode => JsEngineMode.ChakraActiveScript; } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraActiveScript/InteropTests.cs b/test/MsieJavaScriptEngine.Test.ChakraActiveScript/InteropTests.cs index 8c89810..432f127 100644 --- a/test/MsieJavaScriptEngine.Test.ChakraActiveScript/InteropTests.cs +++ b/test/MsieJavaScriptEngine.Test.ChakraActiveScript/InteropTests.cs @@ -1,23 +1,101 @@ -namespace MsieJavaScriptEngine.Test.ChakraActiveScript -{ - using NUnit.Framework; +using System; +using System.IO; + +using NUnit.Framework; - using MsieJavaScriptEngine; - using Common; +using MsieJavaScriptEngine.Test.Common; +using MsieJavaScriptEngine.Test.Common.Interop.Animals; +namespace MsieJavaScriptEngine.Test.ChakraActiveScript +{ [TestFixture] public class InteropTests : InteropTestsBase { - protected override MsieJsEngine CreateJsEngine() + protected override JsEngineMode EngineMode => JsEngineMode.ChakraActiveScript; + + + #region Embedding of objects + + #region Delegates + + [Test] + public override void EmbeddingOfInstanceOfDelegateAndCheckingItsPrototype() + { } + + [Test] + 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.AreEqual("System.String Cry()", TestAllowReflectionSetting(true)); + Assert.AreEqual("undefined", TestAllowReflectionSetting(false)); + } + + #endregion + + #region Recursive calls + + #region Mapping of errors + + [Test] + public void MappingRuntimeErrorDuringRecursiveEvaluationOfFiles() { - var jsEngine = new MsieJsEngine(new JsEngineSettings + // Arrange + string directoryPath = GetAbsolutePath("SharedFiles/recursiveEvaluation/runtimeError"); + const string input = "require('index').calculateResult();"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) { - EngineMode = JsEngineMode.ChakraActiveScript, - UseEcmaScript5Polyfill = false, - UseJson2Library = false - }); + try + { + Func loadModule = path => { + string absolutePath = Path.Combine(directoryPath, $"{path}.js"); + string code = File.ReadAllText(absolutePath); + object result = jsEngine.Evaluate(code, absolutePath); + + return result; + }; + + jsEngine.EmbedHostObject("require", loadModule); + double output = jsEngine.Evaluate(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } - return jsEngine; + // Assert + Assert.NotNull(exception); + Assert.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("'argumens' is undefined", exception.Description); + Assert.AreEqual("ReferenceError", exception.Type); + Assert.AreEqual("math.js", exception.DocumentName); + Assert.AreEqual(10, exception.LineNumber); + Assert.AreEqual(4, exception.ColumnNumber); + Assert.IsEmpty(exception.SourceFragment); + Assert.IsEmpty(exception.CallStack); } + + #endregion + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraActiveScript/MsieJavaScriptEngine.Test.ChakraActiveScript.csproj b/test/MsieJavaScriptEngine.Test.ChakraActiveScript/MsieJavaScriptEngine.Test.ChakraActiveScript.csproj index 52f8f64..2eef93d 100644 --- a/test/MsieJavaScriptEngine.Test.ChakraActiveScript/MsieJavaScriptEngine.Test.ChakraActiveScript.csproj +++ b/test/MsieJavaScriptEngine.Test.ChakraActiveScript/MsieJavaScriptEngine.Test.ChakraActiveScript.csproj @@ -1,76 +1,23 @@ - - - + + - Debug - AnyCPU - {21EA96CC-71E2-4CDD-B95A-C75EBD4AF2FA} + MSIE JavaScript Engine: Tests for Chakra ActiveScript Mode + 3.3.1 + net462 Library - Properties - MsieJavaScriptEngine.Test.ChakraActiveScript - MsieJavaScriptEngine.Test.ChakraActiveScript - v4.0 - 512 - ..\..\ - true + true + false + true + true + false - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll - True - - - - - - - - - - - - - + + + - - {d672bc49-c454-4975-bd25-a555b9bdd793} - MsieJavaScriptEngine - - - {a4085b9e-a5d3-4749-bd07-6d1eb7c23820} - MsieJavaScriptEngine.Test.Common - + + + - - - - - 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/MsieJavaScriptEngine.Test.ChakraActiveScript/MultithreadingTests.cs b/test/MsieJavaScriptEngine.Test.ChakraActiveScript/MultithreadingTests.cs new file mode 100644 index 0000000..21d93f7 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.ChakraActiveScript/MultithreadingTests.cs @@ -0,0 +1,14 @@ +using System; + +using NUnit.Framework; + +using MsieJavaScriptEngine.Test.Common; + +namespace MsieJavaScriptEngine.Test.ChakraActiveScript +{ + [TestFixture] + public class MultithreadingTests : MultithreadingTestsBase + { + protected override JsEngineMode EngineMode => JsEngineMode.ChakraActiveScript; + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraActiveScript/PrecompilationTests.cs b/test/MsieJavaScriptEngine.Test.ChakraActiveScript/PrecompilationTests.cs new file mode 100644 index 0000000..d4c74a2 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.ChakraActiveScript/PrecompilationTests.cs @@ -0,0 +1,12 @@ +using NUnit.Framework; + +using MsieJavaScriptEngine.Test.Common; + +namespace MsieJavaScriptEngine.Test.ChakraActiveScript +{ + [TestFixture] + public class PrecompilationTests : PrecompilationTestsBase + { + protected override JsEngineMode EngineMode => JsEngineMode.ChakraActiveScript; + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraActiveScript/Properties/AssemblyInfo.cs b/test/MsieJavaScriptEngine.Test.ChakraActiveScript/Properties/AssemblyInfo.cs deleted file mode 100644 index e9611bf..0000000 --- a/test/MsieJavaScriptEngine.Test.ChakraActiveScript/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("MsieJavaScriptEngine.Test.ChakraActiveScript")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("MSIE JavaScript Engine: Tests for Chakra ActiveScript Mode")] -[assembly: AssemblyCopyright("Copyright © 2012-2016 Andrey Taritsyn")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("34feac4f-a145-465e-aa36-0eceb5242dd4")] - -[assembly: AssemblyVersion("1.7.0.0")] -[assembly: AssemblyFileVersion("1.7.0.0")] \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraActiveScript/packages.config b/test/MsieJavaScriptEngine.Test.ChakraActiveScript/packages.config deleted file mode 100644 index 512ce05..0000000 --- a/test/MsieJavaScriptEngine.Test.ChakraActiveScript/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/CommonTests.cs b/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/CommonTests.cs index cc37d31..09acc27 100644 --- a/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/CommonTests.cs +++ b/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/CommonTests.cs @@ -1,23 +1,370 @@ -namespace MsieJavaScriptEngine.Test.ChakraEdgeJsRt -{ - using NUnit.Framework; +using System; +using System.Text.RegularExpressions; + +using NUnit.Framework; - using MsieJavaScriptEngine; - using Common; +using MsieJavaScriptEngine.Test.Common; +namespace MsieJavaScriptEngine.Test.ChakraEdgeJsRt +{ [TestFixture] public class CommonTests : CommonTestsBase { - protected override MsieJsEngine CreateJsEngine() + protected override JsEngineMode EngineMode => JsEngineMode.ChakraEdgeJsRt; + + + #region Error handling + + #region Mapping of errors + + [Test] + 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.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Invalid character", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.AreEqual("variables.js", exception.DocumentName); + Assert.AreEqual(3, exception.LineNumber); + Assert.AreEqual(5, exception.ColumnNumber); + Assert.AreEqual("var @variable3 = 678;", exception.SourceFragment); + } + + [Test] + public void MappingCompilationErrorDuringEvaluationOfExpressionInDebugMode() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var @variable3 = 678; + +$variable1 + _variable2 - @variable3;"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine(enableDebugging: true)) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Invalid character", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.AreEqual("variables.js", exception.DocumentName); + Assert.AreEqual(3, exception.LineNumber); + Assert.AreEqual(5, exception.ColumnNumber); + Assert.AreEqual("var @variable3 = 678;", exception.SourceFragment); + } + + [Test] + 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.AreEqual("Runtime error", exception.Category); + Assert.True(Regex.IsMatch(exception.Description, @"^'variable2' is (un|not )defined$")); + Assert.AreEqual("ReferenceError", exception.Type); + Assert.AreEqual("variables.js", exception.DocumentName); + Assert.AreEqual(5, exception.LineNumber); + Assert.AreEqual(1, exception.ColumnNumber); + Assert.IsEmpty(exception.SourceFragment); + Assert.AreEqual(" at Global code (variables.js:5:1)", exception.CallStack); + } + + [Test] + 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.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Syntax error", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.AreEqual("factorial.js", exception.DocumentName); + Assert.AreEqual(10, exception.LineNumber); + Assert.AreEqual(13, exception.ColumnNumber); + Assert.AreEqual("factorial(2%);", exception.SourceFragment); + } + + [Test] + public virtual void MappingCompilationErrorDuringExecutionOfCodeInDebugMode() + { + // 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(enableDebugging: true)) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Syntax error", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.AreEqual("factorial.js", exception.DocumentName); + Assert.AreEqual(10, exception.LineNumber); + Assert.AreEqual(13, exception.ColumnNumber); + Assert.AreEqual("factorial(2%);", exception.SourceFragment); + } + + [Test] + 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.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("The value must be greater than or equal to zero.", exception.Description); + Assert.AreEqual("Error", exception.Type); + Assert.AreEqual("factorial.js", exception.DocumentName); + Assert.AreEqual(3, exception.LineNumber); + Assert.AreEqual(3, exception.ColumnNumber); + Assert.IsEmpty(exception.SourceFragment); + Assert.AreEqual( + " at factorial (factorial.js:3:3)" + Environment.NewLine + + " at Global code (factorial.js:10:1)", + exception.CallStack + ); + } + + #endregion + + #region Generation of error messages + + [Test] + public void GenerationOfCompilationErrorMessage() { - var jsEngine = new MsieJsEngine(new JsEngineSettings + // 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()) { - EngineMode = JsEngineMode.ChakraEdgeJsRt, - UseEcmaScript5Polyfill = false, - UseJson2Library = false - }); + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } - return jsEngine; + Assert.NotNull(exception); + Assert.AreEqual(targetOutput, exception.Message); } + + [Test] + public void GenerationOfCompilationErrorMessageInDebugMode() + { + // 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(enableDebugging: true)) + { + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.AreEqual(targetOutput, exception.Message); + } + + [Test] + 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 targetOutputPattern = @"^ReferenceError: 'bar' is (un|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: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.True(Regex.IsMatch(exception.Message, targetOutputPattern)); + } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/Es5Tests.cs b/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/Es5Tests.cs index b025ee3..cb3a18c 100644 --- a/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/Es5Tests.cs +++ b/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/Es5Tests.cs @@ -1,23 +1,12 @@ -namespace MsieJavaScriptEngine.Test.ChakraEdgeJsRt -{ - using NUnit.Framework; +using NUnit.Framework; - using MsieJavaScriptEngine; - using Common; +using MsieJavaScriptEngine.Test.Common; +namespace MsieJavaScriptEngine.Test.ChakraEdgeJsRt +{ [TestFixture] public class Es5Tests : Es5TestsBase { - protected override MsieJsEngine CreateJsEngine() - { - var jsEngine = new MsieJsEngine(new JsEngineSettings - { - EngineMode = JsEngineMode.ChakraEdgeJsRt, - UseEcmaScript5Polyfill = false, - UseJson2Library = false - }); - - return jsEngine; - } + protected override JsEngineMode EngineMode => JsEngineMode.ChakraEdgeJsRt; } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/InteropTests.cs b/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/InteropTests.cs index f687602..14eefa0 100644 --- a/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/InteropTests.cs +++ b/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/InteropTests.cs @@ -1,23 +1,76 @@ -namespace MsieJavaScriptEngine.Test.ChakraEdgeJsRt -{ - using NUnit.Framework; +using System; +using System.IO; + +using NUnit.Framework; - using MsieJavaScriptEngine; - using Common; +using MsieJavaScriptEngine.Test.Common; +namespace MsieJavaScriptEngine.Test.ChakraEdgeJsRt +{ [TestFixture] public class InteropTests : InteropTestsBase { - protected override MsieJsEngine CreateJsEngine() + protected override JsEngineMode EngineMode => JsEngineMode.ChakraEdgeJsRt; + + + #region Embedding of objects + + #region Recursive calls + + #region Mapping of errors + + [Test] + public void MappingRuntimeErrorDuringRecursiveEvaluationOfFiles() { - var jsEngine = new MsieJsEngine(new JsEngineSettings + // Arrange + string directoryPath = GetAbsolutePath("SharedFiles/recursiveEvaluation/runtimeError"); + const string input = "require('index').calculateResult();"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) { - EngineMode = JsEngineMode.ChakraEdgeJsRt, - UseEcmaScript5Polyfill = false, - UseJson2Library = false - }); + try + { + Func loadModule = path => { + string absolutePath = Path.Combine(directoryPath, $"{path}.js"); + string code = File.ReadAllText(absolutePath); + object result = jsEngine.Evaluate(code, absolutePath); + + return result; + }; + + jsEngine.EmbedHostObject("require", loadModule); + double output = jsEngine.Evaluate(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } - return jsEngine; + // Assert + Assert.NotNull(exception); + Assert.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("'argumens' is not defined", exception.Description); + Assert.AreEqual("ReferenceError", exception.Type); + Assert.AreEqual("math.js", exception.DocumentName); + Assert.AreEqual(10, exception.LineNumber); + Assert.AreEqual(4, exception.ColumnNumber); + Assert.IsEmpty(exception.SourceFragment); + Assert.AreEqual( + " 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 } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/MsieJavaScriptEngine.Test.ChakraEdgeJsRt.csproj b/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/MsieJavaScriptEngine.Test.ChakraEdgeJsRt.csproj index bc7ae77..9863359 100644 --- a/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/MsieJavaScriptEngine.Test.ChakraEdgeJsRt.csproj +++ b/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/MsieJavaScriptEngine.Test.ChakraEdgeJsRt.csproj @@ -1,76 +1,23 @@ - - - + + - Debug - AnyCPU - {16DBAC89-4FFB-40AC-B437-FE5C8D6B6731} + MSIE JavaScript Engine: Tests for Chakra Edge JsRT Mode + 3.3.1 + net462;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0 Library - Properties - MsieJavaScriptEngine.Test.ChakraEdgeJsRt - MsieJavaScriptEngine.Test.ChakraEdgeJsRt - v4.0 - 512 - ..\..\ - true + true + false + true + true + false - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll - True - - - - - - - - - - - - - + + + - - {d672bc49-c454-4975-bd25-a555b9bdd793} - MsieJavaScriptEngine - - - {a4085b9e-a5d3-4749-bd07-6d1eb7c23820} - MsieJavaScriptEngine.Test.Common - + + + - - - - - 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/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/MultithreadingTests.cs b/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/MultithreadingTests.cs new file mode 100644 index 0000000..bd25138 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/MultithreadingTests.cs @@ -0,0 +1,15 @@ +using System; +using System.Text.RegularExpressions; + +using NUnit.Framework; + +using MsieJavaScriptEngine.Test.Common; + +namespace MsieJavaScriptEngine.Test.ChakraEdgeJsRt +{ + [TestFixture] + public class MultithreadingTests : MultithreadingTestsBase + { + protected override JsEngineMode EngineMode => JsEngineMode.ChakraEdgeJsRt; + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/PrecompilationTests.cs b/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/PrecompilationTests.cs new file mode 100644 index 0000000..2b0bd53 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/PrecompilationTests.cs @@ -0,0 +1,208 @@ +using System; +using System.Text.RegularExpressions; + +using NUnit.Framework; + +using MsieJavaScriptEngine.Test.Common; + +namespace MsieJavaScriptEngine.Test.ChakraEdgeJsRt +{ + [TestFixture] + public class PrecompilationTests : PrecompilationTestsBase + { + protected override JsEngineMode EngineMode => JsEngineMode.ChakraEdgeJsRt; + + + #region Error handling + + #region Mapping of errors + + [Test] + 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; +}"; + + PrecompiledScript 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.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Expected ')'", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.AreEqual("guid.js", exception.DocumentName); + Assert.AreEqual(6, exception.LineNumber); + Assert.AreEqual(4, exception.ColumnNumber); + Assert.AreEqual(" ;", exception.SourceFragment); + } + + [Test] + 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 + { + PrecompiledScript precompiledScript = jsEngine.Precompile(input, "getItem.js"); + jsEngine.Execute(precompiledScript); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("Unable to get property '5' of undefined or null reference", exception.Description); + Assert.AreEqual("TypeError", exception.Type); + Assert.AreEqual("getItem.js", exception.DocumentName); + Assert.AreEqual(2, exception.LineNumber); + Assert.AreEqual(2, exception.ColumnNumber); + Assert.IsEmpty(exception.SourceFragment); + Assert.AreEqual( + " at getItem (getItem.js:2:2)" + Environment.NewLine + + " at Anonymous function (getItem.js:9:3)" + Environment.NewLine + + " at Global code (getItem.js:7:2)", + exception.CallStack + ); + } + + #endregion + + #region Generation of error messages + + [Test] + 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 makeId.js:11:2 -> return result;" + ; + + PrecompiledScript precompiledScript = null; + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + precompiledScript = jsEngine.Precompile(input, "makeId.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.Null(precompiledScript); + Assert.NotNull(exception); + Assert.AreEqual(targetOutput, exception.Message); + } + + [Test] + 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 targetOutputPattern = @"^ReferenceError: 'middleName' is (un|not )defined" + Environment.NewLine + + @" at getFullName \(getFullName.js:2:2\)" + Environment.NewLine + + @" at Anonymous function \(getFullName.js:12:2\)" + Environment.NewLine + + @" at Global code \(getFullName.js:7:2\)$" + ; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + PrecompiledScript precompiledScript = jsEngine.Precompile(input, "getFullName.js"); + jsEngine.Execute(precompiledScript); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.True(Regex.IsMatch(exception.Message, targetOutputPattern)); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/Properties/AssemblyInfo.cs b/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/Properties/AssemblyInfo.cs deleted file mode 100644 index a09ae3f..0000000 --- a/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("MsieJavaScriptEngine.Test.ChakraEdgeJsRt")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("MSIE JavaScript Engine: Tests for Chakra Edge JsRT Mode")] -[assembly: AssemblyCopyright("Copyright © 2012-2016 Andrey Taritsyn")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("ebd8a69f-5bb3-47ab-9a8d-b8f35035c278")] - -[assembly: AssemblyVersion("1.7.0.0")] -[assembly: AssemblyFileVersion("1.7.0.0")] \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/packages.config b/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/packages.config deleted file mode 100644 index 512ce05..0000000 --- a/test/MsieJavaScriptEngine.Test.ChakraEdgeJsRt/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/CommonTests.cs b/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/CommonTests.cs index 493eee5..9983e4a 100644 --- a/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/CommonTests.cs +++ b/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/CommonTests.cs @@ -1,23 +1,369 @@ -namespace MsieJavaScriptEngine.Test.ChakraIeJsRt -{ - using NUnit.Framework; +using System; + +using NUnit.Framework; - using MsieJavaScriptEngine; - using Common; +using MsieJavaScriptEngine.Test.Common; +namespace MsieJavaScriptEngine.Test.ChakraIeJsRt +{ [TestFixture] public class CommonTests : CommonTestsBase { - protected override MsieJsEngine CreateJsEngine() + protected override JsEngineMode EngineMode => JsEngineMode.ChakraIeJsRt; + + + #region Error handling + + #region Mapping of errors + + [Test] + 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.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Invalid character", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.IsEmpty(exception.DocumentName); + Assert.AreEqual(3, exception.LineNumber); + Assert.AreEqual(5, exception.ColumnNumber); + Assert.AreEqual("var @variable3 = 678;", exception.SourceFragment); + } + + [Test] + public void MappingCompilationErrorDuringEvaluationOfExpressionInDebugMode() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var @variable3 = 678; + +$variable1 + _variable2 - @variable3;"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine(enableDebugging: true)) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Invalid character", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.IsEmpty(exception.DocumentName); + Assert.AreEqual(3, exception.LineNumber); + Assert.AreEqual(5, exception.ColumnNumber); + Assert.AreEqual("var @variable3 = 678;", exception.SourceFragment); + } + + [Test] + 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.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("'variable2' is undefined", exception.Description); + Assert.AreEqual("ReferenceError", exception.Type); + Assert.AreEqual("variables.js", exception.DocumentName); + Assert.AreEqual(5, exception.LineNumber); + Assert.AreEqual(1, exception.ColumnNumber); + Assert.IsEmpty(exception.SourceFragment); + Assert.AreEqual(" at Global code (variables.js:5:1)", exception.CallStack); + } + + [Test] + 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.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Syntax error", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.IsEmpty(exception.DocumentName); + Assert.AreEqual(10, exception.LineNumber); + Assert.AreEqual(13, exception.ColumnNumber); + Assert.AreEqual("factorial(2%);", exception.SourceFragment); + } + + [Test] + public virtual void MappingCompilationErrorDuringExecutionOfCodeInDebugMode() + { + // 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(enableDebugging: true)) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Syntax error", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.IsEmpty(exception.DocumentName); + Assert.AreEqual(10, exception.LineNumber); + Assert.AreEqual(13, exception.ColumnNumber); + Assert.AreEqual("factorial(2%);", exception.SourceFragment); + } + + [Test] + 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.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("The value must be greater than or equal to zero.", exception.Description); + Assert.AreEqual("Error", exception.Type); + Assert.AreEqual("factorial.js", exception.DocumentName); + Assert.AreEqual(3, exception.LineNumber); + Assert.AreEqual(3, exception.ColumnNumber); + Assert.IsEmpty(exception.SourceFragment); + Assert.AreEqual( + " at factorial (factorial.js:3:3)" + Environment.NewLine + + " at Global code (factorial.js:10:1)", + exception.CallStack + ); + } + + #endregion + + #region Generation of error messages + + [Test] + public void GenerationOfCompilationErrorMessage() { - var jsEngine = new MsieJsEngine(new JsEngineSettings + // 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()) { - EngineMode = JsEngineMode.ChakraIeJsRt, - UseEcmaScript5Polyfill = false, - UseJson2Library = false - }); + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } - return jsEngine; + Assert.NotNull(exception); + Assert.AreEqual(targetOutput, exception.Message); } + + [Test] + public void GenerationOfCompilationErrorMessageInDebugMode() + { + // 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(enableDebugging: true)) + { + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.AreEqual(targetOutput, exception.Message); + } + + [Test] + 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.AreEqual(targetOutput, exception.Message); + } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/Es5Tests.cs b/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/Es5Tests.cs index 12ec415..cb44dd9 100644 --- a/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/Es5Tests.cs +++ b/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/Es5Tests.cs @@ -1,23 +1,12 @@ -namespace MsieJavaScriptEngine.Test.ChakraIeJsRt -{ - using NUnit.Framework; +using NUnit.Framework; - using MsieJavaScriptEngine; - using Common; +using MsieJavaScriptEngine.Test.Common; +namespace MsieJavaScriptEngine.Test.ChakraIeJsRt +{ [TestFixture] public class Es5Tests : Es5TestsBase { - protected override MsieJsEngine CreateJsEngine() - { - var jsEngine = new MsieJsEngine(new JsEngineSettings - { - EngineMode = JsEngineMode.ChakraIeJsRt, - UseEcmaScript5Polyfill = false, - UseJson2Library = false - }); - - return jsEngine; - } + protected override JsEngineMode EngineMode => JsEngineMode.ChakraIeJsRt; } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/InteropTests.cs b/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/InteropTests.cs index 6474b23..15ee2c5 100644 --- a/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/InteropTests.cs +++ b/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/InteropTests.cs @@ -1,23 +1,76 @@ -namespace MsieJavaScriptEngine.Test.ChakraIeJsRt -{ - using NUnit.Framework; +using System; +using System.IO; + +using NUnit.Framework; - using MsieJavaScriptEngine; - using Common; +using MsieJavaScriptEngine.Test.Common; +namespace MsieJavaScriptEngine.Test.ChakraIeJsRt +{ [TestFixture] public class InteropTests : InteropTestsBase { - protected override MsieJsEngine CreateJsEngine() + protected override JsEngineMode EngineMode => JsEngineMode.ChakraIeJsRt; + + + #region Embedding of objects + + #region Recursive calls + + #region Mapping of errors + + [Test] + public void MappingRuntimeErrorDuringRecursiveEvaluationOfFiles() { - var jsEngine = new MsieJsEngine(new JsEngineSettings + // Arrange + string directoryPath = GetAbsolutePath("SharedFiles/recursiveEvaluation/runtimeError"); + const string input = "require('index').calculateResult();"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) { - EngineMode = JsEngineMode.ChakraIeJsRt, - UseEcmaScript5Polyfill = false, - UseJson2Library = false - }); + try + { + Func loadModule = path => { + string absolutePath = Path.Combine(directoryPath, $"{path}.js"); + string code = File.ReadAllText(absolutePath); + object result = jsEngine.Evaluate(code, absolutePath); + + return result; + }; + + jsEngine.EmbedHostObject("require", loadModule); + double output = jsEngine.Evaluate(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } - return jsEngine; + // Assert + Assert.NotNull(exception); + Assert.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("'argumens' is undefined", exception.Description); + Assert.AreEqual("ReferenceError", exception.Type); + Assert.AreEqual("math.js", exception.DocumentName); + Assert.AreEqual(10, exception.LineNumber); + Assert.AreEqual(4, exception.ColumnNumber); + Assert.IsEmpty(exception.SourceFragment); + Assert.AreEqual( + " 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 } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/MsieJavaScriptEngine.Test.ChakraIeJsRt.csproj b/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/MsieJavaScriptEngine.Test.ChakraIeJsRt.csproj index d1a6d53..5e08321 100644 --- a/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/MsieJavaScriptEngine.Test.ChakraIeJsRt.csproj +++ b/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/MsieJavaScriptEngine.Test.ChakraIeJsRt.csproj @@ -1,76 +1,23 @@ - - - + + - Debug - AnyCPU - {7064E0DB-0B73-4534-84D0-1C96DA7E5AD1} + MSIE JavaScript Engine: Tests for Chakra IE JsRT Mode + 3.3.1 + net462;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0 Library - Properties - MsieJavaScriptEngine.Test.ChakraIeJsRt - MsieJavaScriptEngine.Test.ChakraIeJsRt - v4.0 - 512 - ..\..\ - true + true + false + true + true + false - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll - True - - - - - - - - - - - - - + + + - - {d672bc49-c454-4975-bd25-a555b9bdd793} - MsieJavaScriptEngine - - - {a4085b9e-a5d3-4749-bd07-6d1eb7c23820} - MsieJavaScriptEngine.Test.Common - + + + - - - - - 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/MsieJavaScriptEngine.Test.ChakraIeJsRt/MultithreadingTests.cs b/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/MultithreadingTests.cs new file mode 100644 index 0000000..42f5b1d --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/MultithreadingTests.cs @@ -0,0 +1,14 @@ +using System; + +using NUnit.Framework; + +using MsieJavaScriptEngine.Test.Common; + +namespace MsieJavaScriptEngine.Test.ChakraIeJsRt +{ + [TestFixture] + public class MultithreadingTests : MultithreadingTestsBase + { + protected override JsEngineMode EngineMode => JsEngineMode.ChakraIeJsRt; + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/PrecompilationTests.cs b/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/PrecompilationTests.cs new file mode 100644 index 0000000..b9f9b7c --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/PrecompilationTests.cs @@ -0,0 +1,207 @@ +using System; + +using NUnit.Framework; + +using MsieJavaScriptEngine.Test.Common; + +namespace MsieJavaScriptEngine.Test.ChakraIeJsRt +{ + [TestFixture] + public class PrecompilationTests : PrecompilationTestsBase + { + protected override JsEngineMode EngineMode => JsEngineMode.ChakraIeJsRt; + + + #region Error handling + + #region Mapping of errors + + [Test] + 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; +}"; + + PrecompiledScript 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.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Expected ')'", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.AreEqual("guid.js", exception.DocumentName); + Assert.AreEqual(6, exception.LineNumber); + Assert.AreEqual(4, exception.ColumnNumber); + Assert.AreEqual(" ;", exception.SourceFragment); + } + + [Test] + 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 + { + PrecompiledScript precompiledScript = jsEngine.Precompile(input, "getItem.js"); + jsEngine.Execute(precompiledScript); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("Unable to get property '5' of undefined or null reference", exception.Description); + Assert.AreEqual("TypeError", exception.Type); + Assert.AreEqual("getItem.js", exception.DocumentName); + Assert.AreEqual(2, exception.LineNumber); + Assert.AreEqual(2, exception.ColumnNumber); + Assert.IsEmpty(exception.SourceFragment); + Assert.AreEqual( + " at getItem (getItem.js:2:2)" + Environment.NewLine + + " at Anonymous function (getItem.js:9:3)" + Environment.NewLine + + " at Global code (getItem.js:7:2)", + exception.CallStack + ); + } + + #endregion + + #region Generation of error messages + + [Test] + 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 makeId.js:11:2 -> return result;" + ; + + PrecompiledScript precompiledScript = null; + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + precompiledScript = jsEngine.Precompile(input, "makeId.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.Null(precompiledScript); + Assert.NotNull(exception); + Assert.AreEqual(targetOutput, exception.Message); + } + + [Test] + 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 (getFullName.js:2:2)" + Environment.NewLine + + " at Anonymous function (getFullName.js:12:2)" + Environment.NewLine + + " at Global code (getFullName.js:7:2)" + ; + + JsRuntimeException exception = null; + + // Act + using (var jsEngine = CreateJsEngine()) + { + try + { + PrecompiledScript precompiledScript = jsEngine.Precompile(input, "getFullName.js"); + jsEngine.Execute(precompiledScript); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.AreEqual(targetOutput, exception.Message); + } + + #endregion + + #endregion + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/Properties/AssemblyInfo.cs b/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/Properties/AssemblyInfo.cs deleted file mode 100644 index 6dbe669..0000000 --- a/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("MsieJavaScriptEngine.Test.ChakraIeJsRt")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("MSIE JavaScript Engine: Tests for Chakra IE JsRT Mode")] -[assembly: AssemblyCopyright("Copyright © 2012-2016 Andrey Taritsyn")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("2c54c249-206c-45f4-bd1b-6e67a07775af")] - -[assembly: AssemblyVersion("1.7.0.0")] -[assembly: AssemblyFileVersion("1.7.0.0")] \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/packages.config b/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/packages.config deleted file mode 100644 index 512ce05..0000000 --- a/test/MsieJavaScriptEngine.Test.ChakraIeJsRt/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Classic/CommonTests.cs b/test/MsieJavaScriptEngine.Test.Classic/CommonTests.cs index 5396bd7..45a5ac3 100644 --- a/test/MsieJavaScriptEngine.Test.Classic/CommonTests.cs +++ b/test/MsieJavaScriptEngine.Test.Classic/CommonTests.cs @@ -1,23 +1,363 @@ -namespace MsieJavaScriptEngine.Test.Classic -{ - using NUnit.Framework; +using System; + +using NUnit.Framework; - using MsieJavaScriptEngine; - using Common; +using MsieJavaScriptEngine.Test.Common; +namespace MsieJavaScriptEngine.Test.Classic +{ [TestFixture] public class CommonTests : CommonTestsBase { - protected override MsieJsEngine CreateJsEngine() + protected override JsEngineMode EngineMode => JsEngineMode.Classic; + + + #region Error handling + + #region Mapping of errors + + [Test] + 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.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Conditional compilation is turned off", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.AreEqual("variables.js", exception.DocumentName); + Assert.AreEqual(3, exception.LineNumber); + Assert.AreEqual(15, exception.ColumnNumber); + Assert.AreEqual("var @variable3 = 678;", exception.SourceFragment); + } + + [Test] + public void MappingCompilationErrorDuringEvaluationOfExpressionInDebugMode() + { + // Arrange + const string input = @"var $variable1 = 611; +var _variable2 = 711; +var @variable3 = 678; + +$variable1 + _variable2 - @variable3;"; + + JsCompilationException exception = null; + + // Act + using (var jsEngine = CreateJsEngine(enableDebugging: true)) + { + try + { + int result = jsEngine.Evaluate(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Conditional compilation is turned off", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.AreEqual("variables.js", exception.DocumentName); + Assert.AreEqual(3, exception.LineNumber); + Assert.AreEqual(15, exception.ColumnNumber); + Assert.AreEqual("var @variable3 = 678;", exception.SourceFragment); + } + + [Test] + 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.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("'variable2' is undefined", exception.Description); + Assert.AreEqual("TypeError", exception.Type); + Assert.AreEqual("variables.js", exception.DocumentName); + Assert.AreEqual(5, exception.LineNumber); + Assert.AreEqual(1, exception.ColumnNumber); + Assert.IsEmpty(exception.SourceFragment); + Assert.IsEmpty(exception.CallStack); + } + + [Test] + 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.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Syntax error", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.AreEqual("factorial.js", exception.DocumentName); + Assert.AreEqual(10, exception.LineNumber); + Assert.AreEqual(13, exception.ColumnNumber); + Assert.AreEqual("factorial(2%);", exception.SourceFragment); + } + + [Test] + public void MappingCompilationErrorDuringExecutionOfCodeInDebugMode() + { + // 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(enableDebugging: true)) + { + try + { + jsEngine.Execute(input, "factorial.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.AreEqual("Compilation error", exception.Category); + Assert.AreEqual("Syntax error", exception.Description); + Assert.AreEqual("SyntaxError", exception.Type); + Assert.AreEqual("factorial.js", exception.DocumentName); + Assert.AreEqual(10, exception.LineNumber); + Assert.AreEqual(13, exception.ColumnNumber); + Assert.AreEqual("factorial(2%);", exception.SourceFragment); + } + + [Test] + 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.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("The value must be greater than or equal to zero.", exception.Description); + Assert.IsEmpty(exception.Type); + Assert.AreEqual("factorial.js", exception.DocumentName); + Assert.AreEqual(3, exception.LineNumber); + Assert.AreEqual(3, exception.ColumnNumber); + Assert.IsEmpty(exception.SourceFragment); + Assert.IsEmpty(exception.CallStack); + } + + #endregion + + #region Generation of error messages + + [Test] + public void GenerationOfCompilationErrorMessage() { - var jsEngine = new MsieJsEngine(new JsEngineSettings + // 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()) { - EngineMode = JsEngineMode.Classic, - UseEcmaScript5Polyfill = false, - UseJson2Library = false - }); + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } - return jsEngine; + Assert.NotNull(exception); + Assert.AreEqual(targetOutput, exception.Message); } + + [Test] + public void GenerationOfCompilationErrorMessageInDebugMode() + { + // 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(enableDebugging: true)) + { + try + { + jsEngine.Execute(input, "variables.js"); + } + catch (JsCompilationException e) + { + exception = e; + } + } + + Assert.NotNull(exception); + Assert.AreEqual(targetOutput, exception.Message); + } + + [Test] + 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: Object expected" + 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.AreEqual(targetOutput, exception.Message); + } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Classic/Es5Tests.cs b/test/MsieJavaScriptEngine.Test.Classic/Es5Tests.cs index 6b5ed68..9ab2544 100644 --- a/test/MsieJavaScriptEngine.Test.Classic/Es5Tests.cs +++ b/test/MsieJavaScriptEngine.Test.Classic/Es5Tests.cs @@ -1,29 +1,21 @@ -namespace MsieJavaScriptEngine.Test.Classic -{ - using NUnit.Framework; +using NUnit.Framework; - using MsieJavaScriptEngine; - using Common; +using MsieJavaScriptEngine.Test.Common; +namespace MsieJavaScriptEngine.Test.Classic +{ [TestFixture] public class Es5Tests : Es5TestsBase { - protected override MsieJsEngine CreateJsEngine() - { - var jsEngine = new MsieJsEngine(new JsEngineSettings - { - EngineMode = JsEngineMode.Classic, - UseEcmaScript5Polyfill = true, - UseJson2Library = true - }); + protected override JsEngineMode EngineMode => JsEngineMode.Classic; + protected override bool UseEcmaScript5Polyfill => true; + protected override bool UseJson2Library => true; - return jsEngine; - } #region Object methods [Test] - public override void ObjectKeysMethodIsSupported() + public override void SupportsObjectKeysMethod() { // Arrange const string input1 = "Object.keys(['a', 'b', 'c']).toString();"; diff --git a/test/MsieJavaScriptEngine.Test.Classic/InteropTests.cs b/test/MsieJavaScriptEngine.Test.Classic/InteropTests.cs index 2b008ad..48763b5 100644 --- a/test/MsieJavaScriptEngine.Test.Classic/InteropTests.cs +++ b/test/MsieJavaScriptEngine.Test.Classic/InteropTests.cs @@ -1,23 +1,184 @@ -namespace MsieJavaScriptEngine.Test.Classic -{ - using NUnit.Framework; +using System; +using System.IO; + +using NUnit.Framework; - using MsieJavaScriptEngine; - using Common; +using MsieJavaScriptEngine.Test.Common; +using MsieJavaScriptEngine.Test.Common.Interop; +using MsieJavaScriptEngine.Test.Common.Interop.Animals; +namespace MsieJavaScriptEngine.Test.Classic +{ [TestFixture] public class InteropTests : InteropTestsBase { - protected override MsieJsEngine CreateJsEngine() + protected override JsEngineMode EngineMode => JsEngineMode.Classic; + + + #region Embedding of objects + + #region Objects with methods + + [Test] + 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.AreEqual(typeof(Date).FullName, TestAllowReflectionSetting(true)); + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("Object doesn't support this property or method", exception.Description); + } + + [Test] + 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.AreEqual(typeof(Cat).FullName, TestAllowReflectionSetting(true)); + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("Object doesn't support this property or method", exception.Description); + } + + #endregion + + #region Delegates + + [Test] + public override void EmbeddingOfInstanceOfDelegateAndCheckingItsPrototype() + { } + + [Test] + 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.AreEqual("System.String Cry()", TestAllowReflectionSetting(true)); + Assert.AreEqual("undefined", TestAllowReflectionSetting(false)); + } + + #endregion + + #region Recursive calls + + #region Mapping of errors + + [Test] + public void MappingRuntimeErrorDuringRecursiveEvaluationOfFiles() + { + // Arrange + string directoryPath = GetAbsolutePath("SharedFiles/recursiveEvaluation/runtimeError"); + const string input = "require('index').calculateResult();"; + + // Act + JsRuntimeException exception = null; + + using (var jsEngine = CreateJsEngine()) + { + try + { + Func loadModule = path => { + string absolutePath = Path.Combine(directoryPath, $"{path}.js"); + string code = File.ReadAllText(absolutePath); + object result = jsEngine.Evaluate(code, absolutePath); + + return result; + }; + + jsEngine.EmbedHostObject("require", loadModule); + double output = jsEngine.Evaluate(input); + } + catch (JsRuntimeException e) + { + exception = e; + } + } + + // Assert + Assert.NotNull(exception); + Assert.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("'argumens' is undefined", exception.Description); + Assert.AreEqual("TypeError", exception.Type); + Assert.AreEqual("math.js", exception.DocumentName); + Assert.AreEqual(10, exception.LineNumber); + Assert.AreEqual(4, exception.ColumnNumber); + Assert.IsEmpty(exception.SourceFragment); + Assert.IsEmpty(exception.CallStack); + } + + #endregion + + #endregion + + #endregion + + + #region Embedding of types + + #region Creating of instances + + [Test] + public override void CreatingAnInstanceOfEmbeddedCustomExceptionAndCallingOfItsGetTypeMethod() { - var jsEngine = new MsieJsEngine(new JsEngineSettings + // Arrange + string TestAllowReflectionSetting(bool allowReflection) { - EngineMode = JsEngineMode.Classic, - UseEcmaScript5Polyfill = false, - UseJson2Library = false - }); + Type loginFailedExceptionType = typeof(LoginFailedException); + + using (var jsEngine = CreateJsEngine(allowReflection: allowReflection)) + { + jsEngine.EmbedHostType("LoginFailedError", loginFailedExceptionType); + return jsEngine.Evaluate("new LoginFailedError(\"Wrong password entered!\").GetType();"); + } + } - return jsEngine; + // Act and Assert + Assert.AreEqual(typeof(LoginFailedException).FullName, TestAllowReflectionSetting(true)); + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("Object doesn't support this property or method", exception.Description); } + + #endregion + + #endregion } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Classic/MsieJavaScriptEngine.Test.Classic.csproj b/test/MsieJavaScriptEngine.Test.Classic/MsieJavaScriptEngine.Test.Classic.csproj index ac8ffed..585b50e 100644 --- a/test/MsieJavaScriptEngine.Test.Classic/MsieJavaScriptEngine.Test.Classic.csproj +++ b/test/MsieJavaScriptEngine.Test.Classic/MsieJavaScriptEngine.Test.Classic.csproj @@ -1,76 +1,23 @@ - - - + + - Debug - AnyCPU - {50453B82-ACBF-4E25-9582-1113F274D53E} + MSIE JavaScript Engine: Tests for Classic Mode + 3.3.1 + net462 Library - Properties - MsieJavaScriptEngine.Test.Classic - MsieJavaScriptEngine.Test.Classic - v4.0 - 512 - ..\..\ - true + true + false + true + true + false - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll - True - - - - - - - - - - - - - + + + - - {d672bc49-c454-4975-bd25-a555b9bdd793} - MsieJavaScriptEngine - - - {a4085b9e-a5d3-4749-bd07-6d1eb7c23820} - MsieJavaScriptEngine.Test.Common - + + + - - - - - 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/MsieJavaScriptEngine.Test.Classic/MultithreadingTests.cs b/test/MsieJavaScriptEngine.Test.Classic/MultithreadingTests.cs new file mode 100644 index 0000000..8b6577b --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Classic/MultithreadingTests.cs @@ -0,0 +1,14 @@ +using System; + +using NUnit.Framework; + +using MsieJavaScriptEngine.Test.Common; + +namespace MsieJavaScriptEngine.Test.Classic +{ + [TestFixture] + public class MultithreadingTests : MultithreadingTestsBase + { + protected override JsEngineMode EngineMode => JsEngineMode.Classic; + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Classic/PrecompilationTests.cs b/test/MsieJavaScriptEngine.Test.Classic/PrecompilationTests.cs new file mode 100644 index 0000000..3d0540f --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Classic/PrecompilationTests.cs @@ -0,0 +1,12 @@ +using NUnit.Framework; + +using MsieJavaScriptEngine.Test.Common; + +namespace MsieJavaScriptEngine.Test.Classic +{ + [TestFixture] + public class PrecompilationTests : PrecompilationTestsBase + { + protected override JsEngineMode EngineMode => JsEngineMode.Classic; + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Classic/Properties/AssemblyInfo.cs b/test/MsieJavaScriptEngine.Test.Classic/Properties/AssemblyInfo.cs deleted file mode 100644 index dda7ff8..0000000 --- a/test/MsieJavaScriptEngine.Test.Classic/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("MsieJavaScriptEngine.Test.Classic")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("MSIE JavaScript Engine: Tests for Classic Mode")] -[assembly: AssemblyCopyright("Copyright © 2012-2016 Andrey Taritsyn")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("d0f98404-f422-4cab-82a7-6edfce1514d2")] - -[assembly: AssemblyVersion("1.7.0.0")] -[assembly: AssemblyFileVersion("1.7.0.0")] \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Classic/packages.config b/test/MsieJavaScriptEngine.Test.Classic/packages.config deleted file mode 100644 index 512ce05..0000000 --- a/test/MsieJavaScriptEngine.Test.Classic/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/CommonTestsBase.cs b/test/MsieJavaScriptEngine.Test.Common/CommonTestsBase.cs index 8073e06..76ee95e 100644 --- a/test/MsieJavaScriptEngine.Test.Common/CommonTestsBase.cs +++ b/test/MsieJavaScriptEngine.Test.Common/CommonTestsBase.cs @@ -1,21 +1,30 @@ -namespace MsieJavaScriptEngine.Test.Common -{ - using System; - using System.IO; - - using NUnit.Framework; +using System; +using System.IO; +using System.Reflection; +using System.Threading; - using MsieJavaScriptEngine; +using NUnit.Framework; +namespace MsieJavaScriptEngine.Test.Common +{ [TestFixture] public abstract class CommonTestsBase : FileSystemTestsBase { - protected abstract MsieJsEngine CreateJsEngine(); + #region Creation of engines - #region Evaluation of code + [Test] + public virtual void CreationOfEngineWithoutDisposing() + { + MsieJsEngine jsEngine = CreateJsEngine(); + jsEngine.Execute("var a = 1 + 1;"); + } + + #endregion + + #region Evaluation of scripts [Test] - public virtual void EvaluationOfExpressionWithUndefinedResultIsCorrect() + public virtual void EvaluationOfExpressionWithUndefinedResult() { // Arrange const string input = "undefined"; @@ -34,7 +43,7 @@ public virtual void EvaluationOfExpressionWithUndefinedResultIsCorrect() } [Test] - public virtual void EvaluationOfExpressionWithNullResultIsCorrect() + public virtual void EvaluationOfExpressionWithNullResult() { // Arrange const string input = "null"; @@ -53,7 +62,7 @@ public virtual void EvaluationOfExpressionWithNullResultIsCorrect() } [Test] - public virtual void EvaluationOfExpressionWithBooleanResultIsCorrect() + public virtual void EvaluationOfExpressionWithBooleanResult() { // Arrange const string input1 = "7 > 5"; @@ -78,7 +87,7 @@ public virtual void EvaluationOfExpressionWithBooleanResultIsCorrect() } [Test] - public virtual void EvaluationOfExpressionWithIntegerResultIsCorrect() + public virtual void EvaluationOfExpressionWithIntegerResult() { // Arrange const string input = "7 * 8 - 20"; @@ -97,7 +106,7 @@ public virtual void EvaluationOfExpressionWithIntegerResultIsCorrect() } [Test] - public virtual void EvaluationOfExpressionWithDoubleResultIsCorrect() + public virtual void EvaluationOfExpressionWithDoubleResult() { // Arrange const string input = "Math.PI + 0.22"; @@ -116,7 +125,7 @@ public virtual void EvaluationOfExpressionWithDoubleResultIsCorrect() } [Test] - public virtual void EvaluationOfExpressionWithStringResultIsCorrect() + public virtual void EvaluationOfExpressionWithStringResult() { // Arrange const string input = "'Hello, ' + \"Vasya\" + '?';"; @@ -134,12 +143,31 @@ public virtual void EvaluationOfExpressionWithStringResultIsCorrect() Assert.AreEqual(targetOutput, output); } + [Test] + public virtual void EvaluationOfExpressionWithUnicodeStringResult() + { + // Arrange + const string input = "'Привет, ' + \"Вася\" + '?';"; + const string targetOutput = "Привет, Вася?"; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.AreEqual(targetOutput, output); + } + #endregion - #region Execution of code + #region Execution of scripts [Test] - public virtual void ExecutionOfCodeIsCorrect() + public virtual void ExecutionOfCode() { // Arrange const string functionCode = @"function add(num1, num2) { @@ -162,10 +190,10 @@ public virtual void ExecutionOfCodeIsCorrect() } [Test] - public virtual void ExecutionOfFileIsCorrect() + public virtual void ExecutionOfFile() { // Arrange - string filePath = Path.GetFullPath(Path.Combine(_baseDirectoryPath, "SharedFiles/square.js")); + string filePath = GetAbsolutePath("SharedFiles/square.js"); const string input = "square(6);"; const int targetOutput = 36; @@ -183,10 +211,10 @@ public virtual void ExecutionOfFileIsCorrect() } [Test] - public virtual void ExecutionOfResourceByTypeIsCorrect() + public virtual void ExecutionOfResourceByNameAndType() { // Arrange - const string resourceName = "MsieJavaScriptEngine.Test.Common.Resources.cube.js"; + const string resourceName = "Resources.cube.js"; const string input = "cube(5);"; const int targetOutput = 125; @@ -204,7 +232,7 @@ public virtual void ExecutionOfResourceByTypeIsCorrect() } [Test] - public virtual void ExecutionOfResourceByAssemblyIsCorrect() + public virtual void ExecutionOfResourceByNameAndAssembly() { // Arrange const string resourceName = "MsieJavaScriptEngine.Test.Common.Resources.power.js"; @@ -216,7 +244,10 @@ public virtual void ExecutionOfResourceByAssemblyIsCorrect() using (var jsEngine = CreateJsEngine()) { - jsEngine.ExecuteResource(resourceName, typeof(CommonTestsBase).Assembly); + jsEngine.ExecuteResource(resourceName, typeof(CommonTestsBase) + .GetTypeInfo() + .Assembly + ); output = jsEngine.Evaluate(input); } @@ -229,7 +260,7 @@ public virtual void ExecutionOfResourceByAssemblyIsCorrect() #region Calling of functions [Test] - public virtual void CallingOfFunctionWithoutParametersIsCorrect() + public virtual void CallingOfFunctionWithoutParameters() { // Arrange const string functionCode = @"function hooray() { @@ -243,7 +274,7 @@ public virtual void CallingOfFunctionWithoutParametersIsCorrect() using (var jsEngine = CreateJsEngine()) { jsEngine.Execute(functionCode); - output = (string) jsEngine.CallFunction("hooray"); + output = (string)jsEngine.CallFunction("hooray"); } // Assert @@ -251,7 +282,7 @@ public virtual void CallingOfFunctionWithoutParametersIsCorrect() } [Test] - public virtual void CallingOfFunctionWithUndefinedResultIsCorrect() + public virtual void CallingOfFunctionWithUndefinedResult() { // Arrange const string functionCode = @"function testUndefined(value) { @@ -277,7 +308,7 @@ public virtual void CallingOfFunctionWithUndefinedResultIsCorrect() } [Test] - public virtual void CallingOfFunctionWithNullResultIsCorrect() + public virtual void CallingOfFunctionWithNullResult() { // Arrange const string functionCode = @"function testNull(value) { @@ -303,7 +334,7 @@ public virtual void CallingOfFunctionWithNullResultIsCorrect() } [Test] - public virtual void CallingOfFunctionWithBooleanResultIsCorrect() + public virtual void CallingOfFunctionWithBooleanResult() { // Arrange const string functionCode = @"function inverse(value) { @@ -326,7 +357,7 @@ public virtual void CallingOfFunctionWithBooleanResultIsCorrect() } [Test] - public virtual void CallingOfFunctionWithIntegerResultIsCorrect() + public virtual void CallingOfFunctionWithIntegerResult() { // Arrange const string functionCode = @"function negate(value) { @@ -349,7 +380,7 @@ public virtual void CallingOfFunctionWithIntegerResultIsCorrect() } [Test] - public virtual void CallingOfFunctionWithDoubleResultIsCorrect() + public virtual void CallingOfFunctionWithDoubleResult() { // Arrange const string functionCode = @"function triple(value) { @@ -372,7 +403,7 @@ public virtual void CallingOfFunctionWithDoubleResultIsCorrect() } [Test] - public virtual void CallingOfFunctionWithStringResultIsCorrect() + public virtual void CallingOfFunctionWithStringResult() { // Arrange const string functionCode = @"function greeting(name) { @@ -395,7 +426,30 @@ public virtual void CallingOfFunctionWithStringResultIsCorrect() } [Test] - public virtual void CallingOfFunctionWithManyParametersIsCorrect() + 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.AreEqual(targetOutput, output); + } + + [Test] + public virtual void CallingOfFunctionWithManyParameters() { // Arrange const string functionCode = @"function determineArgumentsTypes() { @@ -420,7 +474,7 @@ public virtual void CallingOfFunctionWithManyParametersIsCorrect() using (var jsEngine = CreateJsEngine()) { jsEngine.Execute(functionCode); - output = (string) jsEngine.CallFunction("determineArgumentsTypes", Undefined.Value, null, + output = (string)jsEngine.CallFunction("determineArgumentsTypes", Undefined.Value, null, true, 12, 3.14, "test"); } @@ -429,7 +483,7 @@ public virtual void CallingOfFunctionWithManyParametersIsCorrect() } [Test] - public virtual void CallingOfFunctionWithManyParametersAndBooleanResultIsCorrect() + public virtual void CallingOfFunctionWithManyParametersAndBooleanResult() { // Arrange const string functionCode = @"function and() { @@ -467,7 +521,7 @@ public virtual void CallingOfFunctionWithManyParametersAndBooleanResultIsCorrect } [Test] - public virtual void CallingOfFunctionWithManyParametersAndIntegerResultIsCorrect() + public virtual void CallingOfFunctionWithManyParametersAndIntegerResult() { // Arrange const string functionCode = @"function sum() { @@ -497,7 +551,7 @@ public virtual void CallingOfFunctionWithManyParametersAndIntegerResultIsCorrect } [Test] - public virtual void CallingOfFunctionWithManyParametersAndDoubleResultIsCorrect() + public virtual void CallingOfFunctionWithManyParametersAndDoubleResult() { // Arrange const string functionCode = @"function sum() { @@ -527,7 +581,7 @@ public virtual void CallingOfFunctionWithManyParametersAndDoubleResultIsCorrect( } [Test] - public virtual void CallingOfFunctionWithManyParametersAndStringResultIsCorrect() + public virtual void CallingOfFunctionWithManyParametersAndStringResult() { // Arrange const string functionCode = @"function concatenate() { @@ -556,12 +610,65 @@ public virtual void CallingOfFunctionWithManyParametersAndStringResultIsCorrect( Assert.AreEqual("Hello, Petya!", output); } + [Test] + 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.AreEqual("Привет, Петя!", output); + } + + [Test] + 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.AreEqual(1389, output); + } + #endregion #region Getting, setting and removing variables [Test] - public virtual void SettingAndGettingVariableWithUndefinedValueIsCorrect() + public virtual void SettingAndGettingVariableWithUndefinedValue() { // Arrange const string variableName = "myVar1"; @@ -584,7 +691,7 @@ public virtual void SettingAndGettingVariableWithUndefinedValueIsCorrect() } [Test] - public virtual void SettingAndGettingVariableWithNullValueIsCorrect() + public virtual void SettingAndGettingVariableWithNullValue() { // Arrange const string variableName = "myVar2"; @@ -607,7 +714,7 @@ public virtual void SettingAndGettingVariableWithNullValueIsCorrect() } [Test] - public virtual void SettingAndGettingVariableWithBooleanValueIsCorrect() + public virtual void SettingAndGettingVariableWithBooleanValue() { // Arrange const string variableName = "isVisible"; @@ -641,7 +748,7 @@ public virtual void SettingAndGettingVariableWithBooleanValueIsCorrect() } [Test] - public virtual void SettingAndGettingVariableWithIntegerValueIsCorrect() + public virtual void SettingAndGettingVariableWithIntegerValue() { // Arrange const string variableName = "amount"; @@ -675,7 +782,7 @@ public virtual void SettingAndGettingVariableWithIntegerValueIsCorrect() } [Test] - public virtual void SettingAndGettingVariableWithDoubleValueIsCorrect() + public virtual void SettingAndGettingVariableWithDoubleValue() { // Arrange const string variableName = "price"; @@ -709,7 +816,7 @@ public virtual void SettingAndGettingVariableWithDoubleValueIsCorrect() } [Test] - public virtual void SettingAndGettingVariableWithStringValueIsCorrect() + public virtual void SettingAndGettingVariableWithStringValue() { // Arrange const string variableName = "word"; @@ -743,7 +850,75 @@ public virtual void SettingAndGettingVariableWithStringValueIsCorrect() } [Test] - public virtual void RemovingVariableIsCorrect() + 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.IsTrue(variableExists); + Assert.AreEqual(targetOutput1, output1); + + Assert.AreEqual(input2, output2); + } + + [Test] + 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.IsTrue(variableExists); + Assert.AreEqual(targetOutput1, output1); + + Assert.AreEqual(input2, output2); + } + + [Test] + public virtual void RemovingVariable() { // Arrange const string variableName = "price"; @@ -766,6 +941,103 @@ public virtual void RemovingVariableIsCorrect() Assert.IsFalse(variableAfterRemovingExists); } + [Test] + 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.IsTrue(variableBeforeRemovingExists); + Assert.IsFalse(variableAfterRemovingExists); + } + + #endregion + + #region Script interruption + + [Test] + 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 + Exception currentException = null; + bool output; + + using (var jsEngine = CreateJsEngine()) + { + 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 + Assert.IsInstanceOf(currentException); + Assert.AreEqual(targetOutput, output); + } + + #endregion + + #region Garbage collection + + [Test] + public virtual void GarbageCollection() + { + // Arrange + const string input = @"arr = []; for (i = 0; i < 1000000; i++) { arr.push(arr); }"; + + // Act + using (var jsEngine = CreateJsEngine()) + { + jsEngine.Execute(input); + jsEngine.CollectGarbage(); + } + } + #endregion } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/ErrorFormattingTests.cs b/test/MsieJavaScriptEngine.Test.Common/ErrorFormattingTests.cs new file mode 100644 index 0000000..b3551c0 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/ErrorFormattingTests.cs @@ -0,0 +1,75 @@ +using NUnit.Framework; + +using MsieJavaScriptEngine.Helpers; + +namespace MsieJavaScriptEngine.Test.Common +{ + [TestFixture] + public class ErrorFormattingTests + { + [Test] + 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 - /// Regular expression for working with the `bin` directory path - /// - private readonly Regex _binDirRegex = new Regex(@"\\bin\\(?:Debug|Release)\\?$", RegexOptions.IgnoreCase); - - protected string _baseDirectoryPath; + private string _baseDirectoryPath; protected FileSystemTestsBase() { - string baseDirectoryPath = AppDomain.CurrentDomain.BaseDirectory; - if (_binDirRegex.IsMatch(baseDirectoryPath)) - { - baseDirectoryPath = Path.Combine(baseDirectoryPath, @"..\..\..\"); - } + string appDirectoryPath = AppDomain.CurrentDomain.BaseDirectory; + _baseDirectoryPath = Path.Combine(appDirectoryPath, "../../../../"); + } + + + protected string GetAbsolutePath(string relativePath) + { + string absolutePath = Path.GetFullPath(Path.Combine(_baseDirectoryPath, relativePath)); - _baseDirectoryPath = baseDirectoryPath; + return absolutePath; } } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/Age.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Age.cs new file mode 100644 index 0000000..e556902 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/Interop/Age.cs @@ -0,0 +1,23 @@ +using System; + +namespace MsieJavaScriptEngine.Test.Common.Interop +{ + public struct Age + { + public readonly int Year; + + + public Age(int year) + { + Year = year; + } + + + public override string ToString() + { + int age = DateTime.Now.Year - Year; + + return age.ToString(); + } + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/Animals/AnimalTrainer.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Animals/AnimalTrainer.cs new file mode 100644 index 0000000..3ff6d23 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/Interop/Animals/AnimalTrainer.cs @@ -0,0 +1,10 @@ +namespace MsieJavaScriptEngine.Test.Common.Interop.Animals +{ + public sealed class AnimalTrainer + { + public string ExecuteVoiceCommand(IAnimal animal) + { + return animal.Cry(); + } + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/Animals/Cat.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Animals/Cat.cs new file mode 100644 index 0000000..b7c009a --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/Interop/Animals/Cat.cs @@ -0,0 +1,10 @@ +namespace MsieJavaScriptEngine.Test.Common.Interop.Animals +{ + public sealed class Cat : IAnimal + { + public string Cry() + { + return "Meow!"; + } + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/Animals/Dog.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Animals/Dog.cs new file mode 100644 index 0000000..dbf82b6 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/Interop/Animals/Dog.cs @@ -0,0 +1,10 @@ +namespace MsieJavaScriptEngine.Test.Common.Interop.Animals +{ + public sealed class Dog : IAnimal + { + public string Cry() + { + return "Woof!"; + } + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/Animals/IAnimal.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Animals/IAnimal.cs new file mode 100644 index 0000000..ce697b2 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/Interop/Animals/IAnimal.cs @@ -0,0 +1,7 @@ +namespace MsieJavaScriptEngine.Test.Common.Interop.Animals +{ + public interface IAnimal + { + string Cry(); + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/Base64Encoder.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Base64Encoder.cs index bbc5e6f..d3461bf 100644 --- a/test/MsieJavaScriptEngine.Test.Common/Interop/Base64Encoder.cs +++ b/test/MsieJavaScriptEngine.Test.Common/Interop/Base64Encoder.cs @@ -1,8 +1,8 @@ -namespace MsieJavaScriptEngine.Test.Common.Interop -{ - using System; - using System.Text; +using System; +using System.Text; +namespace MsieJavaScriptEngine.Test.Common.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/MsieJavaScriptEngine.Test.Common/Interop/Date.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Date.cs index c6c4be4..cb92094 100644 --- a/test/MsieJavaScriptEngine.Test.Common/Interop/Date.cs +++ b/test/MsieJavaScriptEngine.Test.Common/Interop/Date.cs @@ -1,11 +1,11 @@ -namespace MsieJavaScriptEngine.Test.Common.Interop -{ - using System; +using System; +namespace MsieJavaScriptEngine.Test.Common.Interop +{ public struct Date { - private static readonly int[] _cumulativeDays = { 0, 31, 59, 90, 120, 151, 181, - 212, 243, 273, 304, 334 }; + private static readonly int[] _cumulativeDays = [0, 31, 59, 90, 120, 151, 181, + 212, 243, 273, 304, 334]; public int Year; public int Month; @@ -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/MsieJavaScriptEngine.Test.Common/Interop/FileManager.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/FileManager.cs index a61aec7..18e2255 100644 --- a/test/MsieJavaScriptEngine.Test.Common/Interop/FileManager.cs +++ b/test/MsieJavaScriptEngine.Test.Common/Interop/FileManager.cs @@ -1,18 +1,26 @@ -namespace MsieJavaScriptEngine.Test.Common.Interop -{ - using System; - using System.IO; +using System; +using System.IO; +using System.Text; +namespace MsieJavaScriptEngine.Test.Common.Interop +{ public sealed class FileManager { public string ReadFile(string path) { - if (path == null) + return ReadFile(path, null); + } + + public string ReadFile(string path, Encoding encoding) + { + if (path is 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/MsieJavaScriptEngine.Test.Common/Interop/Logging/DefaultLogger.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Logging/DefaultLogger.cs new file mode 100644 index 0000000..99ee3bb --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/Interop/Logging/DefaultLogger.cs @@ -0,0 +1,7 @@ +namespace MsieJavaScriptEngine.Test.Common.Interop.Logging +{ + public class DefaultLogger + { + public static ILogger Current = new NullLogger(); + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/Logging/ILogger.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Logging/ILogger.cs new file mode 100644 index 0000000..ede5430 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/Interop/Logging/ILogger.cs @@ -0,0 +1,17 @@ +namespace MsieJavaScriptEngine.Test.Common.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/MsieJavaScriptEngine.Test.Common/Interop/Logging/NullLogger.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Logging/NullLogger.cs new file mode 100644 index 0000000..c66dab1 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/Interop/Logging/NullLogger.cs @@ -0,0 +1,26 @@ +namespace MsieJavaScriptEngine.Test.Common.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/MsieJavaScriptEngine.Test.Common/Interop/Logging/ThrowExceptionLogger.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Logging/ThrowExceptionLogger.cs new file mode 100644 index 0000000..9861687 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/Interop/Logging/ThrowExceptionLogger.cs @@ -0,0 +1,57 @@ +using System; +using System.Text; + +namespace MsieJavaScriptEngine.Test.Common.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/MsieJavaScriptEngine.Test.Common/Interop/LoginFailedException.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/LoginFailedException.cs new file mode 100644 index 0000000..371e6ec --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/Interop/LoginFailedException.cs @@ -0,0 +1,54 @@ +using System; +#if !NET8_0_OR_GREATER +using System.Runtime.Serialization; +#endif + +namespace MsieJavaScriptEngine.Test.Common.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 + + protected LoginFailedException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + if (info is not null) + { + _userName = info.GetString("UserName"); + } + } + + + public override void GetObjectData(SerializationInfo info, StreamingContext context) + { + if (info is null) + { + throw new ArgumentNullException(nameof(info)); + } + + base.GetObjectData(info, context); + info.AddValue("UserName", this._userName); + } +#endif + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/Person.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Person.cs index c5d3862..761673a 100644 --- a/test/MsieJavaScriptEngine.Test.Common/Interop/Person.cs +++ b/test/MsieJavaScriptEngine.Test.Common/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/MsieJavaScriptEngine.Test.Common/Interop/Product.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Product.cs index e86e463..b9c96b4 100644 --- a/test/MsieJavaScriptEngine.Test.Common/Interop/Product.cs +++ b/test/MsieJavaScriptEngine.Test.Common/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/MsieJavaScriptEngine.Test.Common/Interop/RuntimeConstants.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/RuntimeConstants.cs new file mode 100644 index 0000000..e980209 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/Interop/RuntimeConstants.cs @@ -0,0 +1,8 @@ +namespace MsieJavaScriptEngine.Test.Common.Interop +{ + public static class RuntimeConstants + { + public static readonly int MinValue = 0; + public static readonly int MaxValue = 999; + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/SimpleSingleton.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/SimpleSingleton.cs deleted file mode 100644 index 14d1ed4..0000000 --- a/test/MsieJavaScriptEngine.Test.Common/Interop/SimpleSingleton.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace MsieJavaScriptEngine.Test.Common.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/MsieJavaScriptEngine.Test.Common/Interop/SomeClass.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/SomeClass.cs new file mode 100644 index 0000000..8a85635 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/Interop/SomeClass.cs @@ -0,0 +1,10 @@ +namespace MsieJavaScriptEngine.Test.Common.Interop +{ + public class SomeClass + { + public int SomeProperty { get; set; } = 123; + public string SomeOtherProperty { get; set; } = "abc"; + + public static SomeClass Instance { get; } = new SomeClass(); + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/Interop/Temperature.cs b/test/MsieJavaScriptEngine.Test.Common/Interop/Temperature.cs index fc67995..d6d9525 100644 --- a/test/MsieJavaScriptEngine.Test.Common/Interop/Temperature.cs +++ b/test/MsieJavaScriptEngine.Test.Common/Interop/Temperature.cs @@ -1,7 +1,7 @@ -namespace MsieJavaScriptEngine.Test.Common.Interop -{ - using System; +using System; +namespace MsieJavaScriptEngine.Test.Common.Interop +{ public struct Temperature { double _celsius; diff --git a/test/MsieJavaScriptEngine.Test.Common/InteropTestsBase.cs b/test/MsieJavaScriptEngine.Test.Common/InteropTestsBase.cs index 8f57899..883ac04 100644 --- a/test/MsieJavaScriptEngine.Test.Common/InteropTestsBase.cs +++ b/test/MsieJavaScriptEngine.Test.Common/InteropTestsBase.cs @@ -1,27 +1,27 @@ -namespace MsieJavaScriptEngine.Test.Common -{ - 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.Text; - using NUnit.Framework; +using NUnit.Framework; - using MsieJavaScriptEngine; - using Interop; +using MsieJavaScriptEngine.Test.Common.Interop; +using MsieJavaScriptEngine.Test.Common.Interop.Animals; +using MsieJavaScriptEngine.Test.Common.Interop.Logging; +namespace MsieJavaScriptEngine.Test.Common +{ [TestFixture] public abstract class InteropTestsBase : FileSystemTestsBase { - protected abstract MsieJsEngine CreateJsEngine(); - #region Embedding of objects #region Objects with fields [Test] - public virtual void EmbeddingOfInstanceOfCustomValueTypeWithFieldsIsCorrect() + public virtual void EmbeddingOfInstanceOfCustomValueTypeWithFields() { // Arrange var date = new Date(2015, 12, 29); @@ -58,26 +58,57 @@ public virtual void EmbeddingOfInstanceOfCustomValueTypeWithFieldsIsCorrect() } [Test] - public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithFieldsIsCorrect() + public virtual void EmbeddingOfInstanceOfCustomValueTypeWithReadonlyField() + { + // Arrange + var age = new Age(1979); + const string updateCode = "age.Year = 1982;"; + + const string input = "age.Year"; + const int targetOutput = 1979; + + // Act + int output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("age", age); + jsEngine.Execute(updateCode); + + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.AreEqual(targetOutput, output); + } + + [Test] + 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"; - const string input2 = "product.Price"; - const double targetOutput2 = 1144.25; + const string input2 = "product.Description"; + const string targetOutput2 = null; + + const string input3 = "product.Price"; + const double targetOutput3 = 1144.25; // Act string output1; - double output2; + string output2; + double output3; using (var jsEngine = CreateJsEngine()) { @@ -85,12 +116,14 @@ public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithFieldsIsCorrect( jsEngine.Execute(updateCode); output1 = jsEngine.Evaluate(input1); - output2 = jsEngine.Evaluate(input2); + output2 = jsEngine.Evaluate(input2); + output3 = jsEngine.Evaluate(input3); } // Assert Assert.AreEqual(targetOutput1, output1); Assert.AreEqual(targetOutput2, output2); + Assert.AreEqual(targetOutput3, output3); } #endregion @@ -98,7 +131,7 @@ public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithFieldsIsCorrect( #region Objects with properties [Test] - public virtual void EmbeddingOfInstanceOfBuiltinValueTypeWithPropertiesIsCorrect() + public virtual void EmbeddingOfInstanceOfBuiltinValueTypeWithProperties() { // Arrange var timeSpan = new TimeSpan(4840780000000); @@ -139,7 +172,7 @@ public virtual void EmbeddingOfInstanceOfBuiltinValueTypeWithPropertiesIsCorrect } [Test] - public virtual void EmbeddingOfInstanceOfBuiltinReferenceTypeWithPropertiesIsCorrect() + public virtual void EmbeddingOfInstanceOfBuiltinReferenceTypeWithProperties() { // Arrange var uri = new Uri("https://github.com/Taritsyn/MsieJavaScriptEngine"); @@ -174,7 +207,7 @@ public virtual void EmbeddingOfInstanceOfBuiltinReferenceTypeWithPropertiesIsCor } [Test] - public virtual void EmbeddingOfInstanceOfCustomValueTypeWithPropertiesIsCorrect() + public virtual void EmbeddingOfInstanceOfCustomValueTypeWithProperties() { // Arrange var temperature = new Temperature(-17.3, TemperatureUnits.Celsius); @@ -209,11 +242,12 @@ public virtual void EmbeddingOfInstanceOfCustomValueTypeWithPropertiesIsCorrect( } [Test] - public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithPropertiesIsCorrect() + 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"; @@ -221,9 +255,13 @@ public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithPropertiesIsCorr const string input2 = "person.LastName"; const string targetOutput2 = "Ivanoff"; + const string input3 = "person.Patronymic"; + const string targetOutput3 = null; + // Act string output1; string output2; + string output3; using (var jsEngine = CreateJsEngine()) { @@ -232,11 +270,77 @@ public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithPropertiesIsCorr output1 = jsEngine.Evaluate(input1); output2 = jsEngine.Evaluate(input2); + output3 = jsEngine.Evaluate(input3); } // Assert Assert.AreEqual(targetOutput1, output1); Assert.AreEqual(targetOutput2, output2); + Assert.AreEqual(targetOutput3, output3); + } + + [Test] + 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.AreEqual(targetOutput1, output1); + Assert.AreEqual(targetOutput2, output2); + Assert.AreEqual(targetOutput3, output3); + Assert.AreEqual(targetOutput4, output4); + Assert.AreEqual(targetOutput5, output5); + Assert.AreEqual(targetOutput6, output6); } #endregion @@ -244,19 +348,19 @@ public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithPropertiesIsCorr #region Objects with methods [Test] - public virtual void EmbeddingOfInstanceOfBuiltinValueTypeWithMethodsIsCorrect() + 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,9 +371,9 @@ 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 @@ -279,7 +383,7 @@ public virtual void EmbeddingOfInstanceOfBuiltinValueTypeWithMethodsIsCorrect() } [Test] - public virtual void EmbeddingOfInstanceOfBuiltinReferenceTypeWithMethodIsCorrect() + public virtual void EmbeddingOfInstanceOfBuiltinReferenceTypeWithMethod() { // Arrange var random = new Random(); @@ -301,35 +405,42 @@ public virtual void EmbeddingOfInstanceOfBuiltinReferenceTypeWithMethodIsCorrect } [Test] - public virtual void EmbeddingOfInstanceOfCustomValueTypeWithMethodIsCorrect() + 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.AreEqual(targetOutput1, output1); + Assert.AreEqual(targetOutput2, output2); } [Test] - public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithMethodIsCorrect() + public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithMethod() { // Arrange var fileManager = new FileManager(); - string filePath = Path.GetFullPath(Path.Combine(_baseDirectoryPath, "SharedFiles/link.txt")); + string filePath = GetAbsolutePath("SharedFiles/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 @@ -345,12 +456,90 @@ public virtual void EmbeddingOfInstanceOfCustomReferenceTypeWithMethodIsCorrect( Assert.AreEqual(targetOutput, output); } + [Test] + 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.AreEqual(targetOutput1, output1); + Assert.AreEqual(targetOutput2, output2); + } + + [Test] + public virtual 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.AreEqual(typeof(Date).FullName, TestAllowReflectionSetting(true)); + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("Object doesn't support property or method 'GetType'", exception.Description); + } + + [Test] + public virtual 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.AreEqual(typeof(Cat).FullName, TestAllowReflectionSetting(true)); + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("Object doesn't support property or method 'GetType'", exception.Description); + } + #endregion #region Delegates [Test] - public virtual void EmbeddingOfInstanceOfDelegateWithoutParametersIsCorrect() + public virtual void EmbeddingOfInstanceOfDelegateWithoutParameters() { // Arrange var generateRandomStringFunc = new Func(() => @@ -384,12 +573,12 @@ public virtual void EmbeddingOfInstanceOfDelegateWithoutParametersIsCorrect() } // Assert - Assert.IsNotNullOrEmpty(output); + Assert.That(output, Is.Not.Null.Or.Empty); Assert.IsTrue(output.Length == targetOutputLength); } [Test] - public virtual void EmbeddingOfInstanceOfDelegateWithOneParameterIsCorrect() + public virtual void EmbeddingOfInstanceOfDelegateWithOneParameter() { // Arrange var squareFunc = new Func(a => a * a); @@ -411,7 +600,7 @@ public virtual void EmbeddingOfInstanceOfDelegateWithOneParameterIsCorrect() } [Test] - public virtual void EmbeddingOfInstanceOfDelegateWithTwoParametersIsCorrect() + public virtual void EmbeddingOfInstanceOfDelegateWithTwoParameters() { // Arrange var sumFunc = new Func((a, b) => a + b); @@ -432,12 +621,150 @@ public virtual void EmbeddingOfInstanceOfDelegateWithTwoParametersIsCorrect() Assert.AreEqual(targetOutput, output); } + [Test] + 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.AreEqual(targetOutput, output); + Assert.AreEqual(targetLogOutput, logOutput); + } + + [Test] + 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); + } + + [Test] + 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); + } + + [Test] + 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.AreEqual(targetOutput, output); + } + + [Test] + public virtual 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.AreEqual("undefined", TestAllowReflectionSetting(true)); + Assert.AreEqual("undefined", TestAllowReflectionSetting(false)); + } + #endregion #region Integration [Test] - public virtual void InteractionOfEmbeddedCustomValueTypeAndDelegateInstancesIsCorrect() + public virtual void InteractionOfEmbeddedCustomValueTypeAndDelegateInstances() { // Arrange var informaticsDayDate = new Date(2015, 12, 4); @@ -485,6 +812,131 @@ public virtual void InteractionOfEmbeddedCustomValueTypeAndDelegateInstancesIsCo #endregion + #region Recursive calls + + [Test] + public virtual void RecursiveEvaluationOfFiles() + { + // Arrange + string directoryPath = GetAbsolutePath("SharedFiles/recursiveEvaluation/noError"); + const string input = "require('index').calculateResult();"; + const double targetOutput = 132.14; + + // Act + double output; + + using (var jsEngine = CreateJsEngine()) + { + Func loadModule = path => { + string absolutePath = Path.Combine(directoryPath, $"{path}.js"); + string code = File.ReadAllText(absolutePath); + object result = jsEngine.Evaluate(code, absolutePath); + + return result; + }; + + jsEngine.EmbedHostObject("require", loadModule); + output = jsEngine.Evaluate(input); + } + + // Assert + Assert.AreEqual(targetOutput, output); + } + + [Test] + public virtual void RecursiveExecutionOfFiles() + { + // Arrange + string directoryPath = GetAbsolutePath("SharedFiles/recursiveExecution/noError"); + 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, "mainFile.js")); + + output = jsEngine.GetVariableValue(variableName); + } + + // Assert + Assert.AreEqual(targetOutput, output); + } + + #endregion + + #region Removal + + [Test] + 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 + + #region Special cases + + [Test] + public virtual void EmbeddingOfInstanceOfCustomReferenceTypeAndModificationOfGlobalObject() + { + // Arrange + var someObj = new SomeClass(); + + const string modifyingCode = @"(function () { + 'use strict'; + + var global = typeof self != 'undefined' && self.Math == Math ? + self : Function('return this')(); + + global['foo'] = 'bar'; +})();"; + const string variableName = "foo"; + const string targetOutput = "bar"; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostObject("someObj", someObj); + jsEngine.Execute(modifyingCode); + output = jsEngine.GetVariableValue(variableName); + } + + // Assert + Assert.AreEqual(targetOutput, output); + } + + #endregion + #endregion @@ -493,7 +945,7 @@ public virtual void InteractionOfEmbeddedCustomValueTypeAndDelegateInstancesIsCo #region Creating of instances [Test] - public virtual void CreatingAnInstanceOfEmbeddedBuiltinValueTypeIsCorrect() + public virtual void CreatingAnInstanceOfEmbeddedBuiltinValueType() { // Arrange Type pointType = typeof(Point); @@ -515,7 +967,7 @@ public virtual void CreatingAnInstanceOfEmbeddedBuiltinValueTypeIsCorrect() } [Test] - public virtual void CreatingAnInstanceOfEmbeddedBuiltinReferenceTypeIsCorrect() + public virtual void CreatingAnInstanceOfEmbeddedBuiltinReferenceType() { // Arrange Type uriType = typeof(Uri); @@ -541,7 +993,7 @@ public virtual void CreatingAnInstanceOfEmbeddedBuiltinReferenceTypeIsCorrect() } [Test] - public virtual void CreatingAnInstanceOfEmbeddedCustomValueTypeIsCorrect() + public virtual void CreatingAnInstanceOfEmbeddedCustomValueType() { // Arrange Type point3DType = typeof(Point3D); @@ -563,7 +1015,7 @@ public virtual void CreatingAnInstanceOfEmbeddedCustomValueTypeIsCorrect() } [Test] - public virtual void CreatingAnInstanceOfEmbeddedCustomReferenceTypeIsCorrect() + public virtual void CreatingAnInstanceOfEmbeddedCustomReferenceType() { // Arrange Type personType = typeof(Person); @@ -584,12 +1036,55 @@ public virtual void CreatingAnInstanceOfEmbeddedCustomReferenceTypeIsCorrect() Assert.AreEqual(targetOutput, output); } + [Test] + public virtual 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.AreEqual(null, TestAllowReflectionSetting(true)); + Assert.AreEqual("undefined", TestAllowReflectionSetting(false)); + } + + [Test] + public virtual 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.AreEqual(typeof(LoginFailedException).FullName, TestAllowReflectionSetting(true)); + + var exception = Assert.Throws(() => TestAllowReflectionSetting(false)); + Assert.AreEqual("Runtime error", exception.Category); + Assert.AreEqual("Object doesn't support property or method 'GetType'", exception.Description); + } + #endregion #region Types with constants [Test] - public virtual void EmbeddingOfBuiltinReferenceTypeWithConstantsIsCorrect() + public virtual void EmbeddingOfBuiltinReferenceTypeWithConstants() { // Arrange Type mathType = typeof(Math); @@ -618,7 +1113,7 @@ public virtual void EmbeddingOfBuiltinReferenceTypeWithConstantsIsCorrect() } [Test] - public virtual void EmbeddingOfCustomValueTypeWithConstantsIsCorrect() + public virtual void EmbeddingOfCustomValueTypeWithConstants() { // Arrange Type predefinedStringsType = typeof(PredefinedStrings); @@ -653,7 +1148,7 @@ public virtual void EmbeddingOfCustomValueTypeWithConstantsIsCorrect() } [Test] - public virtual void EmbeddingOfCustomReferenceTypeWithConstantIsCorrect() + public virtual void EmbeddingOfCustomReferenceTypeWithConstant() { // Arrange Type base64EncoderType = typeof(Base64Encoder); @@ -679,7 +1174,7 @@ public virtual void EmbeddingOfCustomReferenceTypeWithConstantIsCorrect() #region Types with fields [Test] - public virtual void EmbeddingOfBuiltinValueTypeWithFieldIsCorrect() + public virtual void EmbeddingOfBuiltinValueTypeWithField() { // Arrange Type guidType = typeof(Guid); @@ -701,7 +1196,7 @@ public virtual void EmbeddingOfBuiltinValueTypeWithFieldIsCorrect() } [Test] - public virtual void EmbeddingOfBuiltinReferenceTypeWithFieldIsCorrect() + public virtual void EmbeddingOfBuiltinReferenceTypeWithField() { // Arrange Type bitConverterType = typeof(BitConverter); @@ -723,7 +1218,7 @@ public virtual void EmbeddingOfBuiltinReferenceTypeWithFieldIsCorrect() } [Test] - public virtual void EmbeddingOfCustomValueTypeWithFieldIsCorrect() + public virtual void EmbeddingOfCustomValueTypeWithField() { // Arrange Type point3DType = typeof(Point3D); @@ -745,20 +1240,25 @@ public virtual void EmbeddingOfCustomValueTypeWithFieldIsCorrect() } [Test] - public virtual void EmbeddingOfCustomReferenceTypeWithFieldIsCorrect() + public virtual void EmbeddingOfCustomReferenceTypeWithField() { // Arrange - Type simpleSingletonType = typeof(SimpleSingleton); + Type defaultLoggerType = typeof(DefaultLogger); + Type throwExceptionLoggerType = typeof(ThrowExceptionLogger); + const string updateCode = "DefaultLogger.Current = new ThrowExceptionLogger();"; - 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); + jsEngine.EmbedHostType("DefaultLogger", defaultLoggerType); + jsEngine.EmbedHostType("ThrowExceptionLogger", throwExceptionLoggerType); + jsEngine.Execute(updateCode); + output = jsEngine.Evaluate(input); } @@ -766,12 +1266,44 @@ public virtual void EmbeddingOfCustomReferenceTypeWithFieldIsCorrect() Assert.AreEqual(targetOutput, output); } + [Test] + public virtual void EmbeddingOfCustomReferenceTypeWithReadonlyFields() + { + // Arrange + Type runtimeConstantsType = typeof(RuntimeConstants); + const string updateCode = @"RuntimeConstants.MinValue = 1; +RuntimeConstants.MaxValue = 100;"; + + const string input1 = "RuntimeConstants.MinValue"; + const int targetOutput1 = 0; + + const string input2 = "RuntimeConstants.MaxValue"; + const int targetOutput2 = 999; + + // Act + int output1; + int output2; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostType("RuntimeConstants", runtimeConstantsType); + jsEngine.Execute(updateCode); + + output1 = jsEngine.Evaluate(input1); + output2 = jsEngine.Evaluate(input2); + } + + // Assert + Assert.AreEqual(targetOutput1, output1); + Assert.AreEqual(targetOutput2, output2); + } + #endregion #region Types with properties [Test] - public virtual void EmbeddingOfBuiltinValueTypeWithPropertyIsCorrect() + public virtual void EmbeddingOfBuiltinValueTypeWithProperty() { // Arrange Type colorType = typeof(Color); @@ -793,13 +1325,13 @@ public virtual void EmbeddingOfBuiltinValueTypeWithPropertyIsCorrect() } [Test] - public virtual void EmbeddingOfBuiltinReferenceTypeWithPropertyIsCorrect() + public virtual void EmbeddingOfBuiltinReferenceTypeWithProperty() { // Arrange Type environmentType = typeof(Environment); const string input = "Environment.NewLine"; - string[] targetOutput = { "\r", "\r\n", "\n", "\n\r" }; + string[] targetOutput = ["\r", "\r\n", "\n", "\n\r"]; // Act string output; @@ -815,7 +1347,7 @@ public virtual void EmbeddingOfBuiltinReferenceTypeWithPropertyIsCorrect() } [Test] - public virtual void EmbeddingOfCustomValueTypeWithPropertyIsCorrect() + public virtual void EmbeddingOfCustomValueTypeWithProperty() { // Arrange Type dateType = typeof(Date); @@ -848,7 +1380,7 @@ public virtual void EmbeddingOfCustomValueTypeWithPropertyIsCorrect() } [Test] - public virtual void EmbeddingOfCustomReferenceTypeWithPropertyIsCorrect() + public virtual void EmbeddingOfCustomReferenceTypeWithProperty() { // Arrange Type bundleTableType = typeof(BundleTable); @@ -877,7 +1409,7 @@ public virtual void EmbeddingOfCustomReferenceTypeWithPropertyIsCorrect() #region Types with methods [Test] - public virtual void EmbeddingOfBuiltinValueTypeWithMethodIsCorrect() + public virtual void EmbeddingOfBuiltinValueTypeWithMethod() { // Arrange Type dateTimeType = typeof(DateTime); @@ -899,29 +1431,35 @@ public virtual void EmbeddingOfBuiltinValueTypeWithMethodIsCorrect() } [Test] - public virtual void EmbeddingOfBuiltinReferenceTypeWithMethodIsCorrect() + 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.AreEqual(targetOutput1, output1); + Assert.AreEqual(targetOutput2, output2); } [Test] - public virtual void EmbeddingOfCustomValueTypeWithMethodIsCorrect() + public virtual void EmbeddingOfCustomValueTypeWithMethod() { // Arrange var dateType = typeof(Date); @@ -943,7 +1481,7 @@ public virtual void EmbeddingOfCustomValueTypeWithMethodIsCorrect() } [Test] - public virtual void EmbeddingOfCustomReferenceTypeWithMethodIsCorrect() + public virtual void EmbeddingOfCustomReferenceTypeWithMethod() { // Arrange Type base64EncoderType = typeof(Base64Encoder); @@ -966,6 +1504,72 @@ public virtual void EmbeddingOfCustomReferenceTypeWithMethodIsCorrect() #endregion + #region Removal + + [Test] + 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 + + #region Special cases + + [Test] + public virtual void EmbeddingOfCustomReferenceTypeAndModificationOfGlobalObject() + { + // Arrange + var someType = typeof(SomeClass); + + const string modifyingCode = @"(function () { + 'use strict'; + + var global = typeof self != 'undefined' && self.Math == Math ? + self : Function('return this')(); + + global.foo = 'baz'; +})();"; + const string variableName = "foo"; + const string targetOutput = "baz"; + + // Act + string output; + + using (var jsEngine = CreateJsEngine()) + { + jsEngine.EmbedHostType("SomeType", someType); + jsEngine.Execute(modifyingCode); + output = jsEngine.GetVariableValue(variableName); + } + + // Assert + Assert.AreEqual(targetOutput, output); + } + + #endregion + #endregion } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/MsieJavaScriptEngine.Test.Common.csproj b/test/MsieJavaScriptEngine.Test.Common/MsieJavaScriptEngine.Test.Common.csproj index c724905..56c9455 100644 --- a/test/MsieJavaScriptEngine.Test.Common/MsieJavaScriptEngine.Test.Common.csproj +++ b/test/MsieJavaScriptEngine.Test.Common/MsieJavaScriptEngine.Test.Common.csproj @@ -1,91 +1,34 @@ - - - + + - Debug - AnyCPU - {A4085B9E-A5D3-4749-BD07-6D1EB7C23820} + MSIE JavaScript Engine: Common Tests + 3.3.1 + net462;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0;net10.0 Library - Properties - MsieJavaScriptEngine.Test.Common - MsieJavaScriptEngine.Test.Common - v4.0 - 512 - ..\..\ - true + true + false + true + true + false - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\..\packages\NUnit.2.6.4\lib\nunit.framework.dll - True - - - - - + + + + - - - - - - - - - - - - - - - - + + + + + - - - - - - {d672bc49-c454-4975-bd25-a555b9bdd793} - MsieJavaScriptEngine - + + + + - - - - + - - - - - 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/MsieJavaScriptEngine.Test.Common/MultithreadingTestsBase.cs b/test/MsieJavaScriptEngine.Test.Common/MultithreadingTestsBase.cs new file mode 100644 index 0000000..2b1311c --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/MultithreadingTestsBase.cs @@ -0,0 +1,37 @@ +using System.Threading; + +using NUnit.Framework; + +namespace MsieJavaScriptEngine.Test.Common +{ + [TestFixture] + public abstract class MultithreadingTestsBase : TestsBase + { + [Test] + 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.AreEqual(targetOutput, output); + } + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/PrecompilationTestsBase.cs b/test/MsieJavaScriptEngine.Test.Common/PrecompilationTestsBase.cs new file mode 100644 index 0000000..c6f5186 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/PrecompilationTestsBase.cs @@ -0,0 +1,241 @@ +using System.IO; +using System.Reflection; +using System.Threading.Tasks; + +using NUnit.Framework; + +namespace MsieJavaScriptEngine.Test.Common +{ + [TestFixture] + public abstract class PrecompilationTestsBase : FileSystemTestsBase + { + #region Execution of precompiled scripts + + [Test] + 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 = [0, 1, 42, 600]; + string[] targetOutputStrings = ["секунд", "секунда", "секунды", "секунд"]; + string[] outputStrings = new string[itemCount]; + + // Act + bool supportsScriptPrecompilation = false; + PrecompiledScript precompiledCode = null; + + using (var jsEngine = CreateJsEngine()) + { + supportsScriptPrecompilation = jsEngine.SupportsScriptPrecompilation; + if (supportsScriptPrecompilation) + { + precompiledCode = jsEngine.Precompile(libraryCode, "declinationOfSeconds.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.AreEqual(targetOutputStrings[itemIndex], outputStrings[itemIndex]); + } + } + } + + [Test] + public virtual void ExecutionOfPrecompiledFile() + { + // Arrange + string filePath = GetAbsolutePath("SharedFiles/declinationOfMinutes.js"); + const string functionName = "declinationOfMinutes"; + const int itemCount = 4; + + int[] inputMinutes = [0, 1, 22, 88]; + string[] targetOutputStrings = ["минут", "минута", "минуты", "минут"]; + string[] outputStrings = new string[itemCount]; + + // Act + bool supportsScriptPrecompilation = false; + PrecompiledScript 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.AreEqual(targetOutputStrings[itemIndex], outputStrings[itemIndex]); + } + } + } + + [Test] + public virtual void ExecutionOfPrecompiledResourceByNameAndType() + { + // Arrange + const string resourceName = "Resources.declinationOfHours.js"; + const string functionName = "declinationOfHours"; + const int itemCount = 4; + + int[] inputHours = [0, 1, 24, 48]; + string[] targetOutputStrings = ["часов", "час", "часа", "часов"]; + string[] outputStrings = new string[itemCount]; + + // Act + bool supportsScriptPrecompilation = false; + PrecompiledScript 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.AreEqual(targetOutputStrings[itemIndex], outputStrings[itemIndex]); + } + } + } + + [Test] + public virtual void ExecutionOfPrecompiledResourceByNameAndAssembly() + { + // Arrange + const string resourceName = "MsieJavaScriptEngine.Test.Common.Resources.declinationOfDays.js"; + const string functionName = "declinationOfDays"; + const int itemCount = 4; + + int[] inputDays = [0, 1, 3, 80]; + string[] targetOutputStrings = ["дней", "день", "дня", "дней"]; + string[] outputStrings = new string[itemCount]; + + // Act + bool supportsScriptPrecompilation = false; + PrecompiledScript 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.AreEqual(targetOutputStrings[itemIndex], outputStrings[itemIndex]); + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/Properties/AssemblyInfo.cs b/test/MsieJavaScriptEngine.Test.Common/Properties/AssemblyInfo.cs deleted file mode 100644 index a1d5e5b..0000000 --- a/test/MsieJavaScriptEngine.Test.Common/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("MsieJavaScriptEngine.Test.Common")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("MSIE JavaScript Engine: Common Tests")] -[assembly: AssemblyCopyright("Copyright © 2012-2016 Andrey Taritsyn")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -[assembly: ComVisible(false)] -[assembly: Guid("dad7743e-f458-4009-b96d-26ebcf5146f0")] - -[assembly: AssemblyVersion("1.7.0.0")] -[assembly: AssemblyFileVersion("1.7.0.0")] \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/Resources/declinationOfDays.js b/test/MsieJavaScriptEngine.Test.Common/Resources/declinationOfDays.js new file mode 100644 index 0000000..812f2cb --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/Resources/declinationOfDays.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/MsieJavaScriptEngine.Test.Common/Resources/declinationOfHours.js b/test/MsieJavaScriptEngine.Test.Common/Resources/declinationOfHours.js new file mode 100644 index 0000000..6660462 --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/Resources/declinationOfHours.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/MsieJavaScriptEngine.Test.Common/TestsBase.cs b/test/MsieJavaScriptEngine.Test.Common/TestsBase.cs new file mode 100644 index 0000000..6854dfb --- /dev/null +++ b/test/MsieJavaScriptEngine.Test.Common/TestsBase.cs @@ -0,0 +1,35 @@ +namespace MsieJavaScriptEngine.Test.Common +{ + public abstract class TestsBase + { + /// + /// Gets a JS engine mode + /// + protected abstract JsEngineMode EngineMode { get; } + + /// + /// Gets a flag for whether to use the ECMAScript 5 Polyfill + /// + protected virtual bool UseEcmaScript5Polyfill => false; + + /// + /// Gets a flag for whether to use the JSON2 library + /// + protected virtual bool UseJson2Library => false; + + + protected MsieJsEngine CreateJsEngine(bool allowReflection = false, bool enableDebugging = false) + { + var jsEngine = new MsieJsEngine(new JsEngineSettings + { + AllowReflection = allowReflection, + EnableDebugging = enableDebugging, + EngineMode = EngineMode, + UseEcmaScript5Polyfill = UseEcmaScript5Polyfill, + UseJson2Library = UseJson2Library + }); + + return jsEngine; + } + } +} \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/ValidationTests.cs b/test/MsieJavaScriptEngine.Test.Common/ValidationTests.cs index e108e6b..a9c90a5 100644 --- a/test/MsieJavaScriptEngine.Test.Common/ValidationTests.cs +++ b/test/MsieJavaScriptEngine.Test.Common/ValidationTests.cs @@ -1,14 +1,14 @@ -namespace MsieJavaScriptEngine.Test.Common -{ - using NUnit.Framework; +using NUnit.Framework; - using Helpers; +using MsieJavaScriptEngine.Helpers; +namespace MsieJavaScriptEngine.Test.Common +{ [TestFixture] public class ValidationTests { [Test] - public void NameFormatIsCorrect() + public void CheckingOfCorrectNameFormat() { // Arrange @@ -18,6 +18,7 @@ public void NameFormatIsCorrect() bool name3FormatIsCorrect = ValidationHelpers.CheckNameFormat("fooBar"); bool name4FormatIsCorrect = ValidationHelpers.CheckNameFormat("$grid"); bool name5FormatIsCorrect = ValidationHelpers.CheckNameFormat("a"); + bool name6FormatIsCorrect = ValidationHelpers.CheckNameFormat("À_la_maison"); // Assert Assert.IsTrue(name1FormatIsCorrect); @@ -25,26 +26,74 @@ public void NameFormatIsCorrect() Assert.IsTrue(name3FormatIsCorrect); Assert.IsTrue(name4FormatIsCorrect); Assert.IsTrue(name5FormatIsCorrect); + Assert.IsTrue(name6FormatIsCorrect); + } + + [Test] + 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.IsFalse(name1FormatIsWrong); + Assert.IsFalse(name2FormatIsWrong); + Assert.IsFalse(name3FormatIsWrong); + Assert.IsFalse(name4FormatIsWrong); + Assert.IsFalse(name5FormatIsWrong); + } + + [Test] + 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.IsTrue(documentName1FormatIsCorrect); + Assert.IsTrue(documentName2FormatIsCorrect); + Assert.IsTrue(documentName3FormatIsCorrect); + Assert.IsTrue(documentName4FormatIsCorrect); + Assert.IsTrue(documentName5FormatIsCorrect); + Assert.IsTrue(documentName6FormatIsCorrect); } [Test] - public void NameFormatIsWrong() + public void CheckingOfWrongDocumentNameFormat() { // Arrange // Act - bool name1FormatIsCorrect = ValidationHelpers.CheckNameFormat("good-parts"); - bool name2FormatIsCorrect = ValidationHelpers.CheckNameFormat("1sale"); - bool name3FormatIsCorrect = ValidationHelpers.CheckNameFormat("Foo Bar"); - bool name4FormatIsCorrect = ValidationHelpers.CheckNameFormat("@grid"); - bool name5FormatIsCorrect = ValidationHelpers.CheckNameFormat("2"); + 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.IsFalse(name1FormatIsCorrect); - Assert.IsFalse(name2FormatIsCorrect); - Assert.IsFalse(name3FormatIsCorrect); - Assert.IsFalse(name4FormatIsCorrect); - Assert.IsFalse(name5FormatIsCorrect); + Assert.IsFalse(documentName1FormatIsWrong); + Assert.IsFalse(documentName2FormatIsWrong); + Assert.IsFalse(documentName3FormatIsWrong); + Assert.IsFalse(documentName4FormatIsWrong); + Assert.IsFalse(documentName5FormatIsWrong); + Assert.IsFalse(documentName6FormatIsWrong); } } } \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.Common/packages.config b/test/MsieJavaScriptEngine.Test.Common/packages.config deleted file mode 100644 index 512ce05..0000000 --- a/test/MsieJavaScriptEngine.Test.Common/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/test/MsieJavaScriptEngine.Test.nunit b/test/MsieJavaScriptEngine.Test.nunit deleted file mode 100644 index 0cb3fcb..0000000 --- a/test/MsieJavaScriptEngine.Test.nunit +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/test/SharedFiles/declinationOfMinutes.js b/test/SharedFiles/declinationOfMinutes.js new file mode 100644 index 0000000..41a3e4d --- /dev/null +++ b/test/SharedFiles/declinationOfMinutes.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/SharedFiles/recursiveEvaluation/noError/index.js b/test/SharedFiles/recursiveEvaluation/noError/index.js new file mode 100644 index 0000000..36f3289 --- /dev/null +++ b/test/SharedFiles/recursiveEvaluation/noError/index.js @@ -0,0 +1,18 @@ +/*global require */ +(function () { + 'use strict'; + + function calculateResult() { + var math = require('./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/SharedFiles/recursiveEvaluation/noError/math.js b/test/SharedFiles/recursiveEvaluation/noError/math.js new file mode 100644 index 0000000..829eb44 --- /dev/null +++ b/test/SharedFiles/recursiveEvaluation/noError/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/SharedFiles/recursiveEvaluation/runtimeError/index.js b/test/SharedFiles/recursiveEvaluation/runtimeError/index.js new file mode 100644 index 0000000..36f3289 --- /dev/null +++ b/test/SharedFiles/recursiveEvaluation/runtimeError/index.js @@ -0,0 +1,18 @@ +/*global require */ +(function () { + 'use strict'; + + function calculateResult() { + var math = require('./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/SharedFiles/recursiveEvaluation/runtimeError/math.js b/test/SharedFiles/recursiveEvaluation/runtimeError/math.js new file mode 100644 index 0000000..7bc5a5a --- /dev/null +++ b/test/SharedFiles/recursiveEvaluation/runtimeError/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/SharedFiles/recursiveExecution/noError/firstFile.js b/test/SharedFiles/recursiveExecution/noError/firstFile.js new file mode 100644 index 0000000..9a8443f --- /dev/null +++ b/test/SharedFiles/recursiveExecution/noError/firstFile.js @@ -0,0 +1,2 @@ +num = num * 3; +executeFile(directoryPath + "/secondFile.js"); \ No newline at end of file diff --git a/test/SharedFiles/recursiveExecution/noError/mainFile.js b/test/SharedFiles/recursiveExecution/noError/mainFile.js new file mode 100644 index 0000000..5372239 --- /dev/null +++ b/test/SharedFiles/recursiveExecution/noError/mainFile.js @@ -0,0 +1,2 @@ +var num = 5; +executeFile(directoryPath + "/firstFile.js"); \ No newline at end of file diff --git a/test/SharedFiles/recursiveExecution/noError/secondFile.js b/test/SharedFiles/recursiveExecution/noError/secondFile.js new file mode 100644 index 0000000..8588ae0 --- /dev/null +++ b/test/SharedFiles/recursiveExecution/noError/secondFile.js @@ -0,0 +1 @@ +num -= 3; \ No newline at end of file