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;
            }));
        }
예제 #2
0
        public void JsModuleCanBeImportedAndExecuted()
        {
            var mainModuleName   = "";
            var mainModuleSource = @"
import cube from 'foo';
const global = (new Function('return this;'))();
global.$EXPORTS = cube(3);
";

            var fooModuleSource = @"
export default function cube(x) {
  return x * x * x;
}
";
            var mainModuleReady = false;
            JavaScriptModuleRecord childModuleHandle = JavaScriptModuleRecord.Invalid;

            JavaScriptFetchImportedModuleCallback fetchCallback = (IntPtr referencingModule, IntPtr specifier, out IntPtr dependentModuleRecord) =>
            {
                var moduleName = Engine.GetStringUtf8(new JavaScriptValueSafeHandle(specifier));
                if (string.IsNullOrWhiteSpace(moduleName))
                {
                    dependentModuleRecord = referencingModule;
                    return(false);
                }

                Assert.True(moduleName == "foo");
                var moduleRecord = Engine.JsInitializeModuleRecord(new JavaScriptModuleRecord(referencingModule), new JavaScriptValueSafeHandle(specifier));
                dependentModuleRecord = moduleRecord.DangerousGetHandle();
                childModuleHandle     = moduleRecord;
                return(false);
            };

            JavaScriptFetchImportedModuleFromScriptCallback fetchFromScriptCallback = (IntPtr referencingModule, IntPtr specifier, out IntPtr dependentModuleRecord) =>
            {
                var moduleName = Engine.GetStringUtf8(new JavaScriptValueSafeHandle(specifier));
                if (string.IsNullOrWhiteSpace(moduleName))
                {
                    dependentModuleRecord = referencingModule;
                    return(false);
                }

                Assert.True(moduleName == "foo");
                var moduleRecord = Engine.JsInitializeModuleRecord(new JavaScriptModuleRecord(referencingModule), new JavaScriptValueSafeHandle((IntPtr)specifier));
                dependentModuleRecord = moduleRecord.DangerousGetHandle();
                childModuleHandle     = moduleRecord;
                return(false);
            };

            JavaScriptNotifyModuleReadyCallback notifyCallback = (IntPtr referencingModule, IntPtr exceptionVar) =>
            {
                Assert.Equal(IntPtr.Zero, exceptionVar);
                mainModuleReady = true;
                return(false);
            };

            using (var runtimeHandle = Engine.JsCreateRuntime(JavaScriptRuntimeAttributes.None, null))
            {
                using (var contextHandle = Engine.JsCreateContext(runtimeHandle))
                {
                    Engine.JsSetCurrentContext(contextHandle);

                    //Initialize the "main" module (Empty-string specifier).
                    var moduleNameHandle = Engine.JsCreateString(mainModuleName, (ulong)mainModuleName.Length);
                    var mainModuleHandle = Engine.JsInitializeModuleRecord(JavaScriptModuleRecord.Invalid, moduleNameHandle);
                    Assert.True(mainModuleHandle != JavaScriptModuleRecord.Invalid);

                    //Set the fetch callback.
                    var    fetchCallbackDelegateHandle = GCHandle.Alloc(fetchCallback);
                    IntPtr fetchCallbackPtr            = Marshal.GetFunctionPointerForDelegate(fetchCallback);
                    Engine.JsSetModuleHostInfo(mainModuleHandle, JavaScriptModuleHostInfoKind.FetchImportedModuleCallback, fetchCallbackPtr);

                    //Ensure the callback was set properly.
                    var moduleHostPtr = Engine.JsGetModuleHostInfo(mainModuleHandle, JavaScriptModuleHostInfoKind.FetchImportedModuleCallback);
                    Assert.Equal(fetchCallbackPtr, moduleHostPtr);

                    //Set the fetchScript callback
                    var    fetchFromScriptCallbackDelegateHandle = GCHandle.Alloc(fetchCallback);
                    IntPtr fetchFromScriptCallbackPtr            = Marshal.GetFunctionPointerForDelegate(fetchFromScriptCallback);
                    Engine.JsSetModuleHostInfo(mainModuleHandle, JavaScriptModuleHostInfoKind.FetchImportedModuleFromScriptCallback, fetchFromScriptCallbackPtr);

                    //Ensure the callback was set properly.
                    moduleHostPtr = Engine.JsGetModuleHostInfo(mainModuleHandle, JavaScriptModuleHostInfoKind.FetchImportedModuleFromScriptCallback);
                    Assert.Equal(fetchFromScriptCallbackPtr, moduleHostPtr);

                    //Set the notify callback
                    var    notifyCallbackDelegateHandle = GCHandle.Alloc(fetchCallback);
                    IntPtr notifyCallbackPtr            = Marshal.GetFunctionPointerForDelegate(notifyCallback);
                    Engine.JsSetModuleHostInfo(mainModuleHandle, JavaScriptModuleHostInfoKind.NotifyModuleReadyCallback, notifyCallbackPtr);

                    //Ensure the callback was set properly.
                    moduleHostPtr = Engine.JsGetModuleHostInfo(mainModuleHandle, JavaScriptModuleHostInfoKind.NotifyModuleReadyCallback);
                    Assert.Equal(notifyCallbackPtr, moduleHostPtr);


                    //Indicate the host-defined, main module.
                    Engine.JsSetModuleHostInfo(mainModuleHandle, JavaScriptModuleHostInfoKind.HostDefined, moduleNameHandle.DangerousGetHandle());

                    //Ensure the callback was set properly.
                    moduleHostPtr = Engine.JsGetModuleHostInfo(mainModuleHandle, JavaScriptModuleHostInfoKind.HostDefined);
                    Assert.Equal(moduleNameHandle.DangerousGetHandle(), moduleHostPtr);

                    // ParseModuleSource is sync, while additional fetch & evaluation are async.
                    var scriptBuffer = Encoding.UTF8.GetBytes(mainModuleSource);
                    var errorHandle  = Engine.JsParseModuleSource(mainModuleHandle, JavaScriptSourceContext.GetNextSourceContext(), scriptBuffer, (uint)mainModuleSource.Length, JavaScriptParseModuleSourceFlags.DataIsUTF8);
                    Assert.True(errorHandle == JavaScriptValueSafeHandle.Invalid);
                    Assert.True(childModuleHandle != JavaScriptModuleRecord.Invalid);
                    Assert.False(mainModuleReady);

                    //Parse the foo now.
                    scriptBuffer = Encoding.UTF8.GetBytes(fooModuleSource);
                    errorHandle  = Engine.JsParseModuleSource(childModuleHandle, JavaScriptSourceContext.GetNextSourceContext(), scriptBuffer, (uint)fooModuleSource.Length, JavaScriptParseModuleSourceFlags.DataIsUTF8);
                    Assert.True(errorHandle == JavaScriptValueSafeHandle.Invalid);

                    Assert.True(mainModuleReady);

                    //Now we're ready, evaluate the main module.
                    var evalResultHandle = Engine.JsModuleEvaluation(mainModuleHandle);
                    Assert.True(evalResultHandle != JavaScriptValueSafeHandle.Invalid);

                    //Result type of a module is always undefined per spec.
                    var evalResultType = Engine.JsGetValueType(evalResultHandle);
                    Assert.True(evalResultType == JsValueType.Undefined);

                    var resultHandle = Engine.GetGlobalVariable("$EXPORTS");
                    var handleType   = Engine.JsGetValueType(resultHandle);
                    Assert.True(handleType == JsValueType.Number);

                    var result = Engine.JsNumberToInt(resultHandle);
                    Assert.Equal(27, result);


                    //Cleanup
                    Engine.JsSetModuleHostInfo(mainModuleHandle, JavaScriptModuleHostInfoKind.FetchImportedModuleCallback, IntPtr.Zero);
                    fetchCallbackDelegateHandle.Free();
                    Engine.JsSetModuleHostInfo(mainModuleHandle, JavaScriptModuleHostInfoKind.FetchImportedModuleFromScriptCallback, IntPtr.Zero);
                    fetchFromScriptCallbackDelegateHandle.Free();
                    Engine.JsSetModuleHostInfo(mainModuleHandle, JavaScriptModuleHostInfoKind.NotifyModuleReadyCallback, IntPtr.Zero);
                    notifyCallbackDelegateHandle.Free();
                }
            }
        }