8
8
*/
9
9
10
10
using System ;
11
+ using System . Diagnostics ;
12
+ using System . Reflection ;
11
13
using JavaScriptEngineSwitcher . Core ;
12
14
using JavaScriptEngineSwitcher . Core . Helpers ;
13
15
using Newtonsoft . Json ;
@@ -20,6 +22,20 @@ namespace React
20
22
/// </summary>
21
23
public static class JavaScriptEngineUtils
22
24
{
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
+
23
39
/// <summary>
24
40
/// Determines if the current environment supports the ClearScript V8 engine
25
41
/// </summary>
@@ -61,6 +77,151 @@ Func<Exception, TException> exceptionFactory
61
77
}
62
78
}
63
79
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
+
64
225
/// <summary>
65
226
/// Calls a JavaScript function using the specified engine. If <typeparamref name="T"/> is
66
227
/// not a scalar type, the function is assumed to return a string of JSON that can be
0 commit comments