Esempio n. 1
0
        public JavaScriptValue LoadModule(JSFileObject file, string[] modulesToReload = null)
        {
            if (modulesToReload != null)
            {
                //TODO: For now we just reload all. Later on it would be nice to reaload only the changed ones and the ones
                // referencing them

                foreach (var m in moduleCache.Keys.ToList())
                {
                    if (!_predefinedModules.ContainsKey(m))
                    {
                        moduleCache.Remove(m);
                    }
                }
                //foreach (var m in modulesToReload)
                //{
                //    moduleCache.Remove(m);
                //}
            }

            var rootModule = CreateModule(null, file);

            ParseModuleQueue();

            var returnValue = JavaScriptModuleRecord.RunModule(rootModule);

            ParseModuleQueue();

            return(returnValue);
        }
Esempio n. 2
0
        public void Run(string source)
        {
            JavaScriptModuleRecord root = JavaScriptModuleRecord.Initialize(
                null,
                rootSpecifier
                );

            root.HostUrl = "<root>";

            root.NotifyModuleReadyCallback   = NotifyModuleReadyCallback;
            root.FetchImportedModuleCallBack = FetchImportedModuleCallback;

            try {
                root.ParseSource(source);
            } catch {
                // Exception gets handled by NotifyModuleReadyCallback
            }

            while (moduleParseQueue.Count > 0)
            {
                Loader.Debug("Parsing module from queue...");
                moduleParseQueue.Dequeue() ();
            }

            if (executeRoot != null)
            {
                executeRoot();
                executeRoot = null;
            }
        }
Esempio n. 3
0
        public void RunModule(string script, Func <string, string> loadModuleCallback)
        {
            JavaScriptModuleRecord rootRecord = contextSwitch.With(() =>
            {
                return(createModule(null, null, (name) =>
                {
                    if (string.IsNullOrEmpty(name))
                    {
                        return script;
                    }
                    else
                    {
                        return loadModuleCallback(name);
                    }
                }));
            });

            //startModuleParseQueue();
            moduleReadyEvent.WaitOne();
            contextSwitch.With(() =>
            {
                JavaScriptModuleRecord.RunModule(rootRecord);
                //startModuleParseQueue();//for dynamic import during module run
                //this pattern is not consistent to promise loop pattern, should change it?
            });
        }
Esempio n. 4
0
        public void RunModule(string script, Func <string, string> loadModuleCallback)
        {
            moduleLoadException = null;
            JavaScriptModuleRecord rootRecord = contextSwitch.With(() =>
            {
                return(createModule(null, null, (name) =>
                {
                    if (string.IsNullOrEmpty(name))
                    {
                        return script;
                    }
                    else
                    {
                        return loadModuleCallback(name);
                    }
                }));
            });

            //startModuleParseQueue();
            moduleReadyEvent.WaitOne();
            throwIfExceptionInLoading();
            contextSwitch.With(() =>
            {
                JavaScriptModuleRecord.RunModule(rootRecord);
            });
        }
Esempio n. 5
0
        private JavaScriptModuleRecord CreateModule(JavaScriptModuleRecord?parent, JSFileObject file)
        {
            if (moduleCache.ContainsKey(file.Path))
            {
                return(moduleCache[file.Path]);
            }

            var result = JavaScriptModuleRecord.Create(parent, file.Path);

            moduleCache.Add(file.Path, result);
            result.HostUrl = file.Path;

            JavaScriptModuleRecord.SetFetchModuleCallback(
                result,
                (JavaScriptModuleRecord reference, JavaScriptValue name, out JavaScriptModuleRecord record) =>
            {
                var moduleName = name.ToString();
                var parentPath = reference.HostUrl;

                if (_predefinedModules.ContainsKey(moduleName))
                {
                    record = CreateModule(reference, _predefinedModules[moduleName]);

                    return(JavaScriptErrorCode.NoError);
                }

                var modulePath = string.IsNullOrWhiteSpace(parentPath)
                        ? moduleName
                        : Path.GetFullPath($"{Path.Combine(Path.GetDirectoryName(parentPath), moduleName)}");

                modulePath = RemovePathToResources(modulePath);

                var module = Resources.Load <JSFileObject>(modulePath);

                if (module == null)
                {
                    throw new EntryPointNotFoundException($"Module {modulePath} doesn't exist'");
                }

                record = CreateModule(reference, module);

                return(JavaScriptErrorCode.NoError);
            });

            JavaScriptModuleRecord.SetFetchModuleScriptCallback(result, FetchImportedModuleFromScript);
            JavaScriptModuleRecord.SetNotifyReady(result, ModuleNotifyReady);

            if (file.Path != null)
            {
                _moduleParseQueue.Enqueue(() =>
                {
                    JavaScriptModuleRecord.ParseScript(result, file.Code, _currentSourceContext++);
                    // Debug.Log($"module {file.Path} Parsed");
                });
            }

            // Debug.Log($"{file.Path} module created");
            return(result);
        }
Esempio n. 6
0
        public ModuleLease(JavaScriptModuleRecord module)
        {
            Module = module;

            // Make sure that the JavaScript GC does not collect the module, see
            // https://github.com/microsoft/ChakraCore/wiki/JavaScript-Runtime-(JSRT)-Overview#memory-management
            // https://github.com/Taritsyn/TestChakraCoreEsModules/blob/master/TestChakraCoreEsModules/EsModuleManager.cs#L92
            Module.AddRef();
        }
Esempio n. 7
0
        public JavaScriptValue LoadModule(string fileName)
        {
            var rootModule = createModule(null, fileName);

            parseModuleQueue();
            //return JavaScriptValue.Invalid;
            var x = JavaScriptModuleRecord.RunModule(rootModule);

            parseModuleQueue();
            return(x);
        }
Esempio n. 8
0
        JavaScriptErrorCode FetchImportedModuleCallback(
            JavaScriptModuleRecord referencingModule,
            JavaScriptValue specifierAsValue,
            out JavaScriptModuleRecord outDependentModuleRecord
            )
        {
            string specifier = specifierAsValue.ToString();

            Loader.Debug($"Javascript.Module.RecordDelegate FetchImportedModuleCallback for specifier: '{specifier}'");

            string normalizedSpecifier = resolver.Normalize(referencingModule.HostUrl, specifier);

            JavaScriptModuleRecord dependentModuleRecord;

            if (normalizedSpecifier == null)
            {
                dependentModuleRecord           = JavaScriptModuleRecord.Initialize(null, "<invalid record>");
                dependentModuleRecord.Exception = JavaScriptValue.CreateError($"Could not find module '{specifier}' from '{referencingModule.HostUrl}'");
            }
            else if (cache.Has(normalizedSpecifier))
            {
                dependentModuleRecord = cache.GetOrInvalid(normalizedSpecifier);
            }
            else
            {
                dependentModuleRecord         = JavaScriptModuleRecord.Initialize(referencingModule, normalizedSpecifier);
                dependentModuleRecord.HostUrl = normalizedSpecifier;
                cache.Set(normalizedSpecifier, dependentModuleRecord);

                moduleParseQueue.Enqueue(() => {
                    Loader.Debug($"Javascript.Module.RecordDelegate parsing source for '{specifier}'");
                    string source = "";
                    try {
                        source = resolver.Read(normalizedSpecifier);
                    } catch (Exception loadException) {
                        dependentModuleRecord.Exception = JavaScriptValue.CreateError($"Couldn't read file '{normalizedSpecifier}': " + loadException);
                    }

                    try {
                        // if we couldn't read the file for whatever reason, we must still call ParseSource
                        // so that the internals will move on to the next step and show us the Exception set above
                        dependentModuleRecord.ParseSource(source); // can throw syntax errors
                    } catch {
                        // will set Exception for us!
                    }
                });
            }

            dependencies.Add(dependentModuleRecord);

            outDependentModuleRecord = dependentModuleRecord;
            return(JavaScriptErrorCode.NoError); // Error case gets handled by Exception being set on the module record
        }
Esempio n. 9
0
        JavaScriptErrorCode NotifyModuleReadyCallback(
            JavaScriptModuleRecord referencingModule,
            JavaScriptValue exceptionVar
            )
        {
            Loader.Debug($"Javascript.Module.RecordDelegate NotifyModuleReadyCallback evaluating module: '{referencingModule.HostUrl}'");

            executeRoot = () => {
                referencingModule.Evaluate(); // will throw if exceptionVar was set
            };

            return(JavaScriptErrorCode.NoError); // this return value is ignored! (wiki)
        }
        public BaristaModuleRecord GetBaristaModuleRecord(JavaScriptModuleRecord moduleRecord)
        {
            if (moduleRecord == null)
            {
                throw new ArgumentNullException(nameof(moduleRecord));
            }

            if (m_moduleReferencePool.TryGet(moduleRecord, out BaristaModuleRecord existingModuleRecord))
            {
                return(existingModuleRecord);
            }

            return(null);
        }
        private BaristaModuleRecord CreateBaristaModuleRecordInternal(BaristaContext context, string moduleName, JavaScriptValueSafeHandle specifier, BaristaModuleRecord parentModule = null, bool setAsHost = false, IBaristaModuleLoader moduleLoader = null)
        {
            JavaScriptModuleRecord moduleRecord = null;

            if (m_specifierModuleLookup.ContainsKey(specifier))
            {
                moduleRecord = m_specifierModuleLookup[specifier];
            }

            if (moduleRecord == null || moduleRecord.IsClosed || moduleRecord.IsInvalid)
            {
                if (m_specifierModuleLookup.ContainsKey(specifier))
                {
                    m_specifierModuleLookup.Remove(specifier);
                }

                moduleRecord = m_engine.JsInitializeModuleRecord(parentModule == null ? JavaScriptModuleRecord.Invalid : parentModule.Handle, specifier);
            }

            return(m_moduleReferencePool.GetOrAdd(moduleRecord, () =>
            {
                //If a module loader hasn't be specified in the parameters, obtain one from DI.
                if (moduleLoader == null)
                {
                    moduleLoader = m_serviceProvider.GetRequiredService <IBaristaModuleLoader>();
                }

                var module = new BaristaModuleRecord(moduleName.ToString(), specifier, parentModule, m_engine, context, this, moduleLoader, moduleRecord);
                m_specifierModuleLookup.Add(specifier, moduleRecord);

                //If specified, indicate as host module.
                if (setAsHost == true)
                {
                    m_engine.JsSetModuleHostInfo(moduleRecord, JavaScriptModuleHostInfoKind.HostDefined, specifier.DangerousGetHandle());
                }

                void beforeCollect(object sender, BaristaObjectBeforeCollectEventArgs args)
                {
                    context.BeforeCollect -= beforeCollect;
                    m_moduleReferencePool.RemoveHandle(new JavaScriptModuleRecord(args.Handle));
                    if (sender is BaristaModuleRecord baristaModuleRecord)
                    {
                        m_specifierModuleLookup.Remove(baristaModuleRecord.Specifier);
                    }
                }
                module.BeforeCollect += beforeCollect;
                return module;
            }));
        }
        public string[] ValidateTemplate(string code)
        {
            var context = JavaScriptContext.CreateContext(_runtime);

            context.AddRef();

            try
            {
                return(_dispatcher.Invoke(() =>
                {
                    using (context.GetScope())
                    {
                        Native.ThrowIfError(Native.JsSetPromiseContinuationCallback(_promiseContinuationCallback, IntPtr.Zero));

                        // Load polyfill's
                        JavaScriptContext.RunScript("const globalThis = this;");
                        JavaScriptContext.RunScript(PolyfillScripts.Get("ImageData"));

                        try
                        {
                            var module = JavaScriptModuleRecord.Initialize();
                            module.FetchImportedModuleCallBack = (JavaScriptModuleRecord referencingModule, JavaScriptValue specifier, out JavaScriptModuleRecord dependentModuleRecord) =>
                            {
                                dependentModuleRecord = JavaScriptModuleRecord.Invalid;
                                return JavaScriptErrorCode.NoError;
                            };
                            module.NotifyModuleReadyCallback = (JavaScriptModuleRecord referencingModule, JavaScriptValue exceptionVar) => JavaScriptErrorCode.NoError;
                            module.ParseSource(code);

                            return Array.Empty <string>();
                        }
                        catch (JavaScriptException e) when(e.ErrorCode == JavaScriptErrorCode.ScriptCompile)
                        {
                            return new[] { e.Message };
                        }
                    }
                }));
            }
            finally
            {
                context.Release();
            }
        }
Esempio n. 13
0
        /// <summary>
        ///     User implemented callback to get notification when the module is ready.
        /// </summary>
        /// <remarks>
        ///     The callback is invoked on the current runtime execution thread, therefore execution is blocked until the
        ///     callback completes. This callback should schedule a call to JsEvaluateModule to run the module that has been loaded.
        /// </remarks>
        /// <param name="referencingModule">The referencing module that has finished running ModuleDeclarationInstantiation step.</param>
        /// <param name="exceptionVar">If nullptr, the module is successfully initialized and host should queue the execution job
        ///                            otherwise it's the exception object.</param>
        /// <returns>
        ///     Returns a JsErrorCode - note, the return value is ignored.
        /// </returns>
        /// <see cref="NotifyModuleReadyCallback"></see>
        private JavaScriptErrorCode ModuleLoadedImpl(JavaScriptModuleRecord referencingModule, JavaScriptValue exceptionVar)
        {
            if (!exceptionVar.IsValid)
            {
                _dispatcher.Invoke(() =>
                {
                    using (_context.GetScope())
                    {
                        referencingModule.Evaluate();

                        if (referencingModule.Equals(RootModule))
                        {
                            OnRootModuleEvaluated?.Invoke();
                        }
                    }
                });
            }

            return(JavaScriptErrorCode.NoError);
        }
Esempio n. 14
0
        public void AddPreload(string specifier, string code)
        {
            if (!RootModule.IsValid)
            {
                throw new InvalidOperationException("No root module set");
            }

            var module = JavaScriptModuleRecord.Initialize(RootModule, specifier);

            module.HostUrl = specifier; // Only for debugging
            _moduleLeases.Add(specifier, new ModuleLease(module));

            _dispatcher.Invoke(() =>
            {
                using (_context.GetScope())
                {
                    module.ParseSource(code);
                }
            });
        }
Esempio n. 15
0
        private JavaScriptModuleRecord createModule(JavaScriptModuleRecord?parent, string name)
        {
            var result = API.JavaScriptModuleRecord.Create(parent, name);

            JavaScriptModuleRecord.SetFetchModuleCallback(result, FetchImportedModule);
            JavaScriptModuleRecord.SetFetchModuleScriptCallback(result, FetchImportedModuleFromScript);
            //if (parent==null)//root module, set notify ready callback
            //{
            JavaScriptModuleRecord.SetNotifyReady(result, ModuleNotifyReady);
            //}
            if (name != null)
            {
                moduleParseQueue.Enqueue(() =>
                {
                    JavaScriptModuleRecord.ParseScript(result, loadScript(name));
                    System.Diagnostics.Debug.WriteLine($"module {name} Parsed");
                });
            }

            System.Diagnostics.Debug.WriteLine($"{name} module created");
            return(result);
        }
Esempio n. 16
0
        private JavaScriptModuleRecord createModule(JavaScriptModuleRecord?parent, string name, Func <string, string> loadModuleCallback)
        {
            bool isCreateFromSourceCode = string.IsNullOrEmpty(name);

            //if module is cached, return cached value
            if (!isCreateFromSourceCode && moduleCache.ContainsKey(name))
            {
                return(moduleCache[name]);
            }

            IRuntimeDebuggingService debugService = CurrentNode.GetService <IRuntimeDebuggingService>();
            var result = JavaScriptModuleRecord.Create(parent, name);

            #region init moudle callback delegates
            FetchImportedModuleDelegate fetchImported = (JavaScriptModuleRecord reference, JavaScriptValue scriptName, out JavaScriptModuleRecord output) =>
            {
                output = createModule(reference, scriptName.ToString(), loadModuleCallback);

                return(JavaScriptErrorCode.NoError);
            };

            FetchImportedModuleFromScriptDelegate fetchImportedFromScript = (JavaScriptSourceContext sourceContext, JavaScriptValue scriptName, out JavaScriptModuleRecord output) =>
            {
                output = createModule(null, scriptName.ToString(), loadModuleCallback);
                return(JavaScriptErrorCode.NoError);
            };

            NotifyModuleReadyCallbackDelegate notifyReady = (module, jsvalue) =>
            {
                moduleReadyEvent.Set();
                if (jsvalue.IsValid)
                {
                    var valueService = CurrentNode.GetService <IJSValueService>();
                    valueService.ThrowIfErrorValue(jsvalue);
                }
                debugService.NotifyScriptReady();
                return(JavaScriptErrorCode.NoError);
            };
            #endregion


            Action parseModule = () =>
            {
                string script = loadModuleCallback(name);
                JavaScriptModuleRecord.ParseScript(result, script, debugService.GetScriptContext(name, script));
                //debugService.AddScriptSource(name, script);
                System.Diagnostics.Debug.WriteLine($"module {name} Parsed");
            };

            JavaScriptModuleRecord.SetFetchModuleCallback(result, fetchImported);
            JavaScriptModuleRecord.SetFetchModuleScriptCallback(result, fetchImportedFromScript);
            JavaScriptModuleRecord.SetNotifyReady(result, notifyReady);
            if (!string.IsNullOrEmpty(name))
            {
                JavaScriptModuleRecord.SetHostUrl(result, name);
            }
            else
            {
                JavaScriptModuleRecord.SetHostUrl(result, "<Generated_ModuleRoot>");
            }


            moduleParseQueue.Add(new moduleItem(
                                     parseModule,
                                     fetchImported,
                                     fetchImportedFromScript,
                                     notifyReady));

            if (!isCreateFromSourceCode)
            {
                moduleCache.Add(name, result);//cache the module if it's not directly from RunModule function
            }


            System.Diagnostics.Debug.WriteLine($"{name} module created");
            return(result);
        }
Esempio n. 17
0
        /// <summary>
        ///     User implemented callback to fetch additional imported modules in ES modules.
        /// </summary>
        /// <remarks>
        ///     The callback is invoked on the current runtime execution thread, therefore execution is blocked until
        ///     the callback completes. Notify the host to fetch the dependent module. This is the "import" part
        ///     before HostResolveImportedModule in ES6 spec. This notifies the host that the referencing module has
        ///     the specified module dependency, and the host needs to retrieve the module back.
        ///
        ///     Callback should:
        ///     1. Check if the requested module has been requested before - if yes return the existing
        ///         module record
        ///     2. If no create and initialize a new module record with JsInitializeModuleRecord to
        ///         return and schedule a call to JsParseModuleSource for the new record.
        /// </remarks>
        /// <param name="referencingModule">The referencing module that is requesting the dependent module.</param>
        /// <param name="specifier">The specifier coming from the module source code.</param>
        /// <param name="dependentModuleRecord">The ModuleRecord of the dependent module. If the module was requested
        ///                                     before from other source, return the existing ModuleRecord, otherwise
        ///                                     return a newly created ModuleRecord.</param>
        /// <returns>
        ///     Returns a <c>JsNoError</c> if the operation succeeded an error code otherwise.
        /// </returns>
        /// <see cref="JavaScriptFetchImportedModuleCallBack"/>
        private JavaScriptErrorCode LoadModuleImpl(JavaScriptModuleRecord referencingModule, JavaScriptValue specifier, out JavaScriptModuleRecord dependentModuleRecord)
        {
            var specifierString = specifier.ToString();

            if (_moduleLeases.TryGetValue(specifierString, out var lease))
            {
                dependentModuleRecord = lease.Module;
                return(JavaScriptErrorCode.NoError);
            }

            var alias = ResourceModuleUtils.GetResourceAlias(specifierString);

            if (alias is null)
            {
                dependentModuleRecord           = JavaScriptModuleRecord.Initialize(referencingModule, specifier);
                dependentModuleRecord.HostUrl   = specifierString; // Only for debugging
                dependentModuleRecord.Exception = JavaScriptValue.CreateTypeError($"Failed to resolve module for specifier '{specifierString}'");
                _moduleLeases.Add(specifierString, new ModuleLease(dependentModuleRecord));
                dependentModuleRecord = _moduleLeases[specifierString].Module;
                return(JavaScriptErrorCode.NoError);
            }

            dependentModuleRecord         = JavaScriptModuleRecord.Initialize(referencingModule, specifier);
            dependentModuleRecord.HostUrl = specifierString; // Only for debugging
            _moduleLeases.Add(specifierString, new ModuleLease(dependentModuleRecord));

            // Fire off a task in the threadpool
            Task.Run(async() =>
            {
                var module = _moduleLeases[specifierString].Module;
                try
                {
                    var resource = await _resourceManager.GetResourceAsync(alias);

                    if (resource is object)
                    {
                        var script = _resourceScriptFactory.CreateFromExtension(resource, Path.GetExtension(specifierString));

                        _dispatcher.Invoke(() =>
                        {
                            using (_context.GetScope())
                            {
                                module.ParseSource(script);
                            }
                        });
                    }
                    else
                    {
                        _dispatcher.Invoke(() =>
                        {
                            using (_context.GetScope())
                            {
                                ThrowModuleException(module, JavaScriptValue.CreateError($"Could not find the resource '{specifierString}'"));
                            }
                        });
                    }
                }
                catch (Exception e)
                {
                    _dispatcher.Invoke(() =>
                    {
                        using (_context.GetScope())
                        {
                            ThrowModuleException(module, JavaScriptValue.CreateError(e.Message));
                        }
                    });
                }

                void ThrowModuleException(JavaScriptModuleRecord module, JavaScriptValue error)
                {
                    error.AddRef();
                    module.Exception = error;

                    if (!JavaScriptContext.HasException)
                    {
                        JavaScriptContext.SetException(error);
                    }

                    try
                    {
                        Native.ThrowIfError(JavaScriptErrorCode.ScriptException);
                    }
                    catch (Exception e)
                    {
                        OnResourceLoadError?.Invoke(e);
                    }
                    finally
                    {
                        error.Release();
                    }
                }
            });

            return(JavaScriptErrorCode.NoError);
        }
Esempio n. 18
0
 private JavaScriptErrorCode FetchImportedModuleFromScript(JavaScriptSourceContext sourceContext, JavaScriptValue source, out JavaScriptModuleRecord result)
 {
     // Debug.Log("FetchImportedModuleFromScriptDelegate start");
     result = new JavaScriptModuleRecord();
     return(JavaScriptErrorCode.NoError);
 }
Esempio n. 19
0
 public void Set(String normalizedSpecifier, JavaScriptModuleRecord record)
 {
     Loader.Debug($"Javascript.Module.Cache Set('{normalizedSpecifier}', record)");
     dict.Add(normalizedSpecifier, record);
 }
Esempio n. 20
0
 private JavaScriptErrorCode FetchImportedModule(JavaScriptModuleRecord reference, JavaScriptValue name, out JavaScriptModuleRecord result)
 {
     System.Diagnostics.Debug.WriteLine($"FetchImportedModule [{name.ToString()}]");
     result = createModule(reference, name.ToString());
     return(JavaScriptErrorCode.NoError);
 }
Esempio n. 21
0
 private JavaScriptErrorCode FetchImportedModuleFromScript(JavaScriptSourceContext sourceContext, JavaScriptValue source, out JavaScriptModuleRecord result)
 {
     System.Diagnostics.Debug.WriteLine("FetchImportedModuleFromScriptDelegate start");
     result = new JavaScriptModuleRecord();
     return(JavaScriptErrorCode.NoError);
 }
        public async Task <GenerateResult> GenerateDocumentAsync(string code, object model, IResourceManager resourceManager = null, CancellationToken cancellationToken = default)
        {
            // Multiple contexts can share a runtime, but only one thread can access the runtime at a time
            var context = JavaScriptContext.CreateContext(_runtime);

            context.AddRef();
            ModuleLease rootModuleLease = null;

            try
            {
                using var loader = new CustomLoader(context, _dispatcher, _resourceScriptFactory, resourceManager);
                var tcs = new TaskCompletionSource <JavaScriptValue>(TaskCreationOptions.RunContinuationsAsynchronously);

                _dispatcher.Invoke(() =>
                {
                    using (context.GetScope())
                    {
                        Native.ThrowIfError(Native.JsSetPromiseContinuationCallback(_promiseContinuationCallback, IntPtr.Zero));

                        // Load polyfill's
                        JavaScriptContext.RunScript("const globalThis = this;");
                        JavaScriptContext.RunScript(PolyfillScripts.Get("ImageData"));

                        var rootModule = JavaScriptModuleRecord.Initialize();
                        rootModule.AddRef();
                        rootModule.HostUrl = "<host>"; // Only for debugging

                        loader.RootModule = rootModule;
                        rootModule.FetchImportedModuleCallBack = loader.LoadModule;
                        rootModule.NotifyModuleReadyCallback   = loader.ModuleLoaded;
                        rootModuleLease = new ModuleLease(rootModule);

                        loader.AddPreload("main", code);
                    }
                });

                // Add callback that will be run when the root module has been evaluated,
                // at which time its rootModule.Namespace becomes valid.
                loader.OnRootModuleEvaluated = () =>
                {
                    using (context.GetScope())
                    {
                        var build = rootModuleLease.Module.Namespace.GetProperty("build");
                        build.AddRef();

                        var modelJson = JavaScriptValue.FromString(JsonSerializer.Serialize(model));
                        modelJson.AddRef();

                        var resultPromise = build.CallFunction(JavaScriptValue.GlobalObject, modelJson);
                        resultPromise.AddRef();

                        FunctionLease resolve = default;
                        FunctionLease reject  = default;
                        FunctionLease always  = default;

                        resolve = new FunctionLease((callee, isConstructCall, arguments, argumentCount, callbackState) =>
                        {
                            var result = arguments[1];

                            result.AddRef();
                            tcs.SetResult(result);

                            return(callee);
                        });

                        reject = new FunctionLease((callee, isConstructCall, arguments, argumentCount, callbackState) =>
                        {
                            JavaScriptException exception;

                            if (arguments.Length >= 2)
                            {
                                var reason = arguments[1];
                                if (reason.ValueType == JavaScriptValueType.Error)
                                {
                                    exception = new JavaScriptException(JavaScriptErrorCode.ScriptException, reason.GetProperty("message").ToString());
                                }
                                else
                                {
                                    exception = new JavaScriptException(JavaScriptErrorCode.ScriptException, reason.ConvertToString().ToString());
                                }
                            }
                            else
                            {
                                exception = new JavaScriptException(JavaScriptErrorCode.ScriptException);
                            }

                            tcs.SetException(exception);

                            return(callee);
                        });

                        always = new FunctionLease((callee, isConstructCall, arguments, argumentCount, callbackState) =>
                        {
                            build.Release();
                            modelJson.Release();
                            resultPromise.Release();

                            resolve.Dispose();
                            reject.Dispose();
                            always.Dispose();

                            return(callee);
                        });

                        resultPromise
                        .GetProperty("then").CallFunction(resultPromise, resolve.Function)
                        .GetProperty("catch").CallFunction(resultPromise, reject.Function)
                        .GetProperty("finally").CallFunction(resultPromise, always.Function);
                    }
                };
                loader.OnResourceLoadError = e => tcs.SetException(e);

                _dispatcher.Invoke(() =>
                {
                    using (context.GetScope())
                    {
                        rootModuleLease.Module.ParseSource(@"
import Builder from 'main';

const builder = new Builder();
const build = async (modelJson) => {
const model = JSON.parse(modelJson);
const content = await Promise.resolve(builder.build(model));
try {
const { contentType } = await import('main');
return { content, contentType };
} catch (error) {
return { content };
}
};

export { build };
");
                    }
                });

                var result = await tcs.Task;

                return(_dispatcher.Invoke(() =>
                {
                    using (context.GetScope())
                    {
                        _runtime.CollectGarbage();

                        var content = result.GetProperty("content");
                        var contentTypeValue = result.GetProperty("contentType");
                        var contentType = contentTypeValue.ValueType == JavaScriptValueType.String ? contentTypeValue.ToString() : null;

                        switch (content.ValueType)
                        {
                        case JavaScriptValueType.String: return new GenerateResult()
                            {
                                Content = Encoding.UTF8.GetBytes(content.ToString()), ContentType = contentType ?? "text/plain"
                            };

                        case JavaScriptValueType.TypedArray:
                            {
                                content.GetTypedArrayInfo(out var arrayType, out var buffer, out var byteOffset, out var byteLength);

                                if (arrayType != JavaScriptTypedArrayType.Uint8)
                                {
                                    throw new NotSupportedException("Typed array must be Uint8Array");
                                }

                                var bufferPointer = buffer.GetArrayBufferStorage(out var bufferLength);
                                var array = new byte[byteLength];
                                Marshal.Copy(IntPtr.Add(bufferPointer, (int)byteOffset), array, 0, (int)byteLength);

                                return new GenerateResult()
                                {
                                    Content = array, ContentType = contentType ?? "application/octet-stream"
                                };
                            }

                        case JavaScriptValueType.Array:
                            {
                                var list = content.ToList();
                                var array = new byte[list.Count];
                                for (var i = 0; i < list.Count; i++)
                                {
                                    array[i] = (byte)list[i].ToInt32();
                                }

                                return new GenerateResult()
                                {
                                    Content = array, ContentType = contentType ?? "application/octet-stream"
                                };
                            }

                        default: throw new NotSupportedException("Build did not produce a supported result");
                        }
                    }
                }));
            }
            finally
            {
                _dispatcher.Invoke(() =>
                {
                    using (context.GetScope())
                    {
                        rootModuleLease?.Dispose();
                    }
                });
                context.Release();
            }
        }
Esempio n. 23
0
 private JavaScriptErrorCode ModuleNotifyReady(JavaScriptModuleRecord module, JavaScriptValue value)
 {
     System.Diagnostics.Debug.WriteLine("ModuleNotifyReady");
     return(JavaScriptErrorCode.NoError);
 }
Esempio n. 24
0
 private JavaScriptErrorCode ModuleNotifyReady(JavaScriptModuleRecord module, JavaScriptValue value)
 {
     // Debug.Log("ModuleNotifyReady");
     return(JavaScriptErrorCode.NoError);
 }