88 */
99
1010using System ;
11+ using System . Diagnostics ;
12+ using System . Reflection ;
1113using JavaScriptEngineSwitcher . Core ;
1214using JavaScriptEngineSwitcher . Core . Helpers ;
1315using Newtonsoft . Json ;
@@ -20,6 +22,20 @@ namespace React
2022 /// </summary>
2123 public static class JavaScriptEngineUtils
2224 {
25+ /// <summary>
26+ /// Cache key for the script resource precompilation
27+ /// </summary>
28+ private const string PRECOMPILED_JS_RESOURCE_CACHE_KEY = "PRECOMPILED_JS_RESOURCE_{0}" ;
29+ /// <summary>
30+ /// Cache key for the script file precompilation
31+ /// </summary>
32+ private const string PRECOMPILED_JS_FILE_CACHE_KEY = "PRECOMPILED_JS_FILE_{0}" ;
33+ /// <summary>
34+ /// Value that indicates whether a cache entry, that contains a precompiled script, should be
35+ /// evicted if it has not been accessed in a given span of time
36+ /// </summary>
37+ private readonly static TimeSpan PRECOMPILED_JS_CACHE_ENTRY_SLIDING_EXPIRATION = TimeSpan . FromMinutes ( 30 ) ;
38+
2339 /// <summary>
2440 /// Determines if the current environment supports the ClearScript V8 engine
2541 /// </summary>
@@ -61,6 +77,151 @@ Func<Exception, TException> exceptionFactory
6177 }
6278 }
6379
80+ /// <summary>
81+ /// Executes a code from JavaScript file.
82+ /// </summary>
83+ /// <param name="engine">Engine to execute code from JavaScript file</param>
84+ /// <param name="fileSystem">File system wrapper</param>
85+ /// <param name="path">Path to the JavaScript file</param>
86+ public static void ExecuteFile ( this IJsEngine engine , IFileSystem fileSystem , string path )
87+ {
88+ var contents = fileSystem . ReadAsString ( path ) ;
89+ engine . Execute ( contents , path ) ;
90+ }
91+
92+ /// <summary>
93+ /// Tries to execute a code with pre-compilation.
94+ /// </summary>
95+ /// <param name="engine">Engine to execute code with pre-compilation</param>
96+ /// <param name="cache">Cache used for storing the pre-compiled scripts</param>
97+ /// <param name="fileSystem">File system wrapper</param>
98+ /// <param name="code">JavaScript code</param>
99+ /// <param name="path">Path to the file on which the executable code depends (required
100+ /// for cache management).</param>
101+ /// <returns>true if can perform a script pre-compilation; otherwise, false.</returns>
102+ public static bool TryExecuteWithPrecompilation ( this IJsEngine engine , ICache cache , IFileSystem fileSystem ,
103+ string code , string path )
104+ {
105+ if ( ! CheckPrecompilationAvailability ( engine , cache ) )
106+ {
107+ return false ;
108+ }
109+
110+ var cacheKey = string . Format ( PRECOMPILED_JS_FILE_CACHE_KEY , path ) ;
111+ var precompiledScript = cache . Get < IPrecompiledScript > ( cacheKey ) ;
112+
113+ if ( precompiledScript == null )
114+ {
115+ precompiledScript = engine . Precompile ( code , path ) ;
116+ var fullPath = fileSystem . MapPath ( path ) ;
117+ cache . Set (
118+ cacheKey ,
119+ precompiledScript ,
120+ slidingExpiration : PRECOMPILED_JS_CACHE_ENTRY_SLIDING_EXPIRATION ,
121+ cacheDependencyFiles : new [ ] { fullPath }
122+ ) ;
123+ }
124+
125+ engine . Execute ( precompiledScript ) ;
126+
127+ return true ;
128+ }
129+
130+ /// <summary>
131+ /// Tries to execute a code from JavaScript file with pre-compilation.
132+ /// </summary>
133+ /// <param name="engine">Engine to execute code from JavaScript file with pre-compilation</param>
134+ /// <param name="cache">Cache used for storing the pre-compiled scripts</param>
135+ /// <param name="fileSystem">File system wrapper</param>
136+ /// <param name="path">Path to the JavaScript file</param>
137+ /// <returns>true if can perform a script pre-compilation; otherwise, false.</returns>
138+ public static bool TryExecuteFileWithPrecompilation ( this IJsEngine engine , ICache cache ,
139+ IFileSystem fileSystem , string path )
140+ {
141+ if ( ! CheckPrecompilationAvailability ( engine , cache ) )
142+ {
143+ return false ;
144+ }
145+
146+ var cacheKey = string . Format ( PRECOMPILED_JS_FILE_CACHE_KEY , path ) ;
147+ var precompiledScript = cache . Get < IPrecompiledScript > ( cacheKey ) ;
148+
149+ if ( precompiledScript == null )
150+ {
151+ var contents = fileSystem . ReadAsString ( path ) ;
152+ precompiledScript = engine . Precompile ( contents , path ) ;
153+ var fullPath = fileSystem . MapPath ( path ) ;
154+ cache . Set (
155+ cacheKey ,
156+ precompiledScript ,
157+ slidingExpiration : PRECOMPILED_JS_CACHE_ENTRY_SLIDING_EXPIRATION ,
158+ cacheDependencyFiles : new [ ] { fullPath }
159+ ) ;
160+ }
161+
162+ engine . Execute ( precompiledScript ) ;
163+
164+ return true ;
165+ }
166+
167+ /// <summary>
168+ /// Tries to execute a code from embedded JavaScript resource with pre-compilation.
169+ /// </summary>
170+ /// <param name="engine">Engine to execute a code from embedded JavaScript resource with pre-compilation</param>
171+ /// <param name="cache">Cache used for storing the pre-compiled scripts</param>
172+ /// <param name="resourceName">The case-sensitive resource name</param>
173+ /// <param name="assembly">The assembly, which contains the embedded resource</param>
174+ /// <returns>true if can perform a script pre-compilation; otherwise, false.</returns>
175+ public static bool TryExecuteResourceWithPrecompilation ( this IJsEngine engine , ICache cache ,
176+ string resourceName , Assembly assembly )
177+ {
178+ if ( ! CheckPrecompilationAvailability ( engine , cache ) )
179+ {
180+ return false ;
181+ }
182+
183+ var cacheKey = string . Format ( PRECOMPILED_JS_RESOURCE_CACHE_KEY , resourceName ) ;
184+ var precompiledScript = cache . Get < IPrecompiledScript > ( cacheKey ) ;
185+
186+ if ( precompiledScript == null )
187+ {
188+ precompiledScript = engine . PrecompileResource ( resourceName , assembly ) ;
189+ cache . Set (
190+ cacheKey ,
191+ precompiledScript ,
192+ slidingExpiration : PRECOMPILED_JS_CACHE_ENTRY_SLIDING_EXPIRATION
193+ ) ;
194+ }
195+
196+ engine . Execute ( precompiledScript ) ;
197+
198+ return true ;
199+ }
200+
201+ /// <summary>
202+ /// Checks a availability of the script pre-compilation
203+ /// </summary>
204+ /// <param name="engine">Instance of the JavaScript engine</param>
205+ /// <param name="cache">Cache used for storing the pre-compiled scripts</param>
206+ /// <returns>true if the script pre-compilation is available; otherwise, false.</returns>
207+ private static bool CheckPrecompilationAvailability ( IJsEngine engine , ICache cache )
208+ {
209+ if ( ! engine . SupportsScriptPrecompilation )
210+ {
211+ Trace . WriteLine ( string . Format ( "The {0} version {1} does not support the script pre-compilation." ,
212+ engine . Name , engine . Version ) ) ;
213+ return false ;
214+ }
215+
216+ if ( cache is NullCache )
217+ {
218+ Trace . WriteLine ( "Usage of script pre-compilation without caching does not make sense." ) ;
219+ return false ;
220+ }
221+
222+ return true ;
223+ }
224+
64225 /// <summary>
65226 /// Calls a JavaScript function using the specified engine. If <typeparamref name="T"/> is
66227 /// not a scalar type, the function is assumed to return a string of JSON that can be
0 commit comments