/// <summary>
 /// Instantiates the <see cref="NativeJavaScriptExecutor"/>.
 /// </summary>
 /// <param name="useSerialization">true to use serialization, else false.</param>
 public NativeJavaScriptExecutor(bool useSerialization)
 {
     _executor = new ChakraBridge.NativeJavaScriptExecutor();
     Native.ThrowIfError((JavaScriptErrorCode)_executor.InitializeHost());
     _useSerialization = useSerialization;
 }
        /// <summary>
        /// Runs the JavaScript at the given path.
        /// </summary>
        /// <param name="sourcePath">The source path.</param>
        /// <param name="sourceUrl">The source URL.</param>
        public void RunScript(string sourcePath, string sourceUrl)
        {
            if (sourcePath == null)
            {
                throw new ArgumentNullException(nameof(sourcePath));
            }
            if (sourceUrl == null)
            {
                throw new ArgumentNullException(nameof(sourceUrl));
            }

            try
            {
                var binPath = Path.Combine(ApplicationData.Current.LocalFolder.Path, BytecodeFileName);

                if (_useSerialization)
                {
                    var srcFileInfo = new FileInfo(sourcePath);
                    var binFileInfo = new FileInfo(binPath);

                    bool ranSuccessfully = false;
                    // The idea is to run the JS bundle and generate bytecode for it on a background thread.
                    // This eliminates the need to delay the first start when the app doesn't have bytecode.
                    // Next time the app starts, it checks if bytecode is still good and  runs it directly.
                    if (binFileInfo.Exists && binFileInfo.LastWriteTime > srcFileInfo.LastWriteTime)
                    {
                        try
                        {
                            Native.ThrowIfError((JavaScriptErrorCode)_executor.RunSerializedScript(sourcePath, binPath, sourceUrl));
                            ranSuccessfully = true;
                        }
                        catch (JavaScriptUsageException exc)
                        {
                            if (exc.ErrorCode == JavaScriptErrorCode.BadSerializedScript)
                            {
                                // Bytecode format is dependent on Chakra engine version, so an OS upgrade may require a recompilation
                                RnLog.Warn(ReactConstants.RNW, $"Serialized bytecode script is corrupted or wrong format, will generate new one");
                            }
                            else
                            {
                                // Some more severe error. We still have a chance (recompiling), so we keep continuing.
                                RnLog.Error(ReactConstants.RNW, exc, $"Failed to run serialized bytecode script, will generate new one");
                            }

                            File.Delete(binPath);
                        }
                    }
                    else
                    {
                        RnLog.Info(ReactConstants.RNW, $"Serialized bytecode script doesn't exist or is obsolete, will generate one");
                    }

                    if (!ranSuccessfully)
                    {
                        Task.Run(() =>
                        {
                            try
                            {
                                // In Chakra JS engine, only one runtime can be active on a particular thread at a time,
                                // and a runtime can only be active on one thread at a time. However it's possible to
                                // create two runtimes and let them run on different threads.
                                var rt = new ChakraBridge.NativeJavaScriptExecutor();

                                Native.ThrowIfError((JavaScriptErrorCode)rt.InitializeHost());
                                Native.ThrowIfError((JavaScriptErrorCode)rt.SerializeScript(sourcePath, binPath));
                                Native.ThrowIfError((JavaScriptErrorCode)rt.DisposeHost());
                            }
                            catch (Exception ex)
                            {
                                RnLog.Error(ReactConstants.RNW, ex, $"Failed to generate serialized bytecode script.");
                                // It's fine if the bytecode couldn't be generated: RN can still use the JS bundle.
                            }
                        });

                        Native.ThrowIfError((JavaScriptErrorCode)_executor.RunScript(sourcePath, sourceUrl));
                    }
                }
                else
                {
                    Native.ThrowIfError((JavaScriptErrorCode)_executor.RunScript(sourcePath, sourceUrl));
                }
            }
            catch (JavaScriptScriptException ex)
            {
                var jsonError  = JavaScriptValueToJTokenConverter.Convert(ex.Error);
                var message    = jsonError.Value <string>("message");
                var stackTrace = jsonError.Value <string>("stack");
                throw new Modules.Core.JavaScriptException(message ?? ex.Message, stackTrace, ex);
            }
        }