/// <summary> /// Attempts to execute the provided JavaScript code using a non-pooled JavaScript engine (ie. /// creates a new JS engine per-thread). This is because Babel uses a LOT of memory, so we /// should completely dispose any engines that have loaded Babel in order to conserve memory. /// /// If an exception is thrown, retries the execution using a new thread (and hence a new engine) /// with a larger maximum stack size. /// This is required because JSXTransformer uses a huge stack which ends up being larger /// than what ASP.NET allows by default (256 KB). /// </summary> /// <typeparam name="T">Type to return from JavaScript call</typeparam> /// <param name="function">JavaScript function to execute</param> /// <param name="args">Arguments to pass to function</param> /// <returns>Result returned from JavaScript code</returns> public virtual T ExecuteWithBabel <T>(string function, params object[] args) { var engine = _engineFactory.GetEngineForCurrentThread(); EnsureBabelLoaded(engine); #if NET40 || NET45 || NETSTANDARD2_0 try { return(engine.CallFunctionReturningJson <T>(function, args)); } catch (Exception) { // Assume the exception MAY be an "out of stack space" error. Try running the code // in a different thread with larger stack. If the same exception occurs, we know // it wasn't a stack space issue. T result = default(T); Exception innerEx = null; var thread = new Thread(() => { // New engine will be created here (as this is a new thread) var threadEngine = _engineFactory.GetEngineForCurrentThread(); EnsureBabelLoaded(threadEngine); try { result = threadEngine.CallFunctionReturningJson <T>(function, args); } catch (Exception threadEx) { // Unhandled exceptions in threads kill the whole process. // Pass the exception back to the parent thread to rethrow. innerEx = threadEx; } finally { _engineFactory.DisposeEngineForCurrentThread(); } }, LARGE_STACK_SIZE); thread.Start(); thread.Join(); // Rethrow any exceptions that occured in the thread if (innerEx != null) { throw innerEx; } return(result); } #else return(engine.CallFunctionReturningJson <T>(function, args)); #endif }
/// <summary> /// Attempts to execute the provided JavaScript code using the current engine. If an /// exception is thrown, retries the execution using a new thread (and hence a new engine) /// with a larger maximum stack size. /// This is required because JSXTransformer uses a huge stack which ends up being larger /// than what ASP.NET allows by default (256 KB). /// </summary> /// <typeparam name="T">Type to return from JavaScript call</typeparam> /// <param name="function">JavaScript function to execute</param> /// <param name="args">Arguments to pass to function</param> /// <returns>Result returned from JavaScript code</returns> public virtual T ExecuteWithLargerStackIfRequired <T>(string function, params object[] args) { // This hack is not required when pooling JavaScript engines, since pooled MSIE // engines already execute on their own thread with a larger stack. if (_config.ReuseJavaScriptEngines) { return(Execute <T>(function, args)); } try { return(Execute <T>(function, args)); } catch (Exception) { // Assume the exception MAY be an "out of stack space" error. Try running the code // in a different thread with larger stack. If the same exception occurs, we know // it wasn't a stack space issue. T result = default(T); Exception innerEx = null; var thread = new Thread(() => { // New engine will be created here (as this is a new thread) var engine = _engineFactory.GetEngineForCurrentThread(); try { result = engine.CallFunction <T>(function, args); } catch (Exception threadEx) { // Unhandled exceptions in threads kill the whole process. // Pass the exception back to the parent thread to rethrow. innerEx = threadEx; } finally { _engineFactory.DisposeEngineForCurrentThread(); } }, LARGE_STACK_SIZE); thread.Start(); thread.Join(); // Rethrow any exceptions that occured in the thread if (innerEx != null) { throw innerEx; } return(result); } }