elringus / bootsharp

Compile C# solution into single-file ES module with auto-generated JavaScript bindings and type definitions
https://sharp.elringus.com
MIT License
673 stars 36 forks source link

Roslyn Workspace support #150

Closed knervous closed 7 months ago

knervous commented 7 months ago

I am trying to create an AdhocWorkspace to generate some completion data from Roslyn for the Monaco editor. I do have this working in Blazor atm but I'm new to the whole ecosystem. Big fan of this package already for the way it plugs in as a portable JS module, would love to be able to get this working here as well.

I am trying

MefHostServices host = MefHostServices.Create(MefHostServices.DefaultAssemblies);
var workspace = new AdhocWorkspace(host);

With the package versions

  <ItemGroup>
    <PackageReference Include="Microsoft.CodeAnalysis.Common" Version="4.9.2" />
    <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Features" Version="4.9.2" />
    <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.9.2" />
    <PackageReference Include="Microsoft.CodeAnalysis.Workspaces.Common" Version="4.9.2" />
  </ItemGroup>

And I am getting this error. Have tried embedded / non-embedded in the browser and in node.js and get the same error.

Uncaught    at System.Composition.Hosting.Providers.Metadata.MetadataViewProvider.GetMetadataViewProvider[WorkspaceServiceMetadata]()
   at System.Composition.Hosting.Providers.Lazy.LazyWithMetadataExportDescriptorProvider.GetLazyDefinitions[IWorkspaceService,WorkspaceServiceMetadata](CompositionContract lazyContract, DependencyAccessor definitionAccessor)
   at System.Composition.Hosting.Providers.Lazy.LazyWithMetadataExportDescriptorProvider.GetExportDescriptors(CompositionContract exportKey, DependencyAccessor definitionAccessor)
   at System.Composition.Hosting.Core.ExportDescriptorRegistryUpdate.GetPromises(CompositionContract )
   at System.Composition.Hosting.Core.DependencyAccessor.ResolveDependencies(Object , CompositionContract , Boolean )
   at System.Composition.Hosting.Providers.ImportMany.ImportManyExportDescriptorProvider.<>c__DisplayClass3_0`1[[System.Lazy`2[[Microsoft.CodeAnalysis.Host.IWorkspaceService, Microsoft.CodeAnalysis.Workspaces, Version=4.9.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35],[Microsoft.CodeAnalysis.Host.Mef.WorkspaceServiceMetadata, Microsoft.CodeAnalysis.Workspaces, Version=4.9.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].<GetImportManyDescriptor>b__0()
   at System.Composition.Hosting.Core.ExportDescriptorPromise.<>c__DisplayClass6_0.<.ctor>b__0()
   at System.Lazy`1[[System.Collections.ObjectModel.ReadOnlyCollection`1[[System.Composition.Hosting.Core.CompositionDependency, System.Composition.Hosting, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].ViaFactory(LazyThreadSafetyMode )
   at System.Lazy`1[[System.Collections.ObjectModel.ReadOnlyCollection`1[[System.Composition.Hosting.Core.CompositionDependency, System.Composition.Hosting, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].CreateValue()
   at System.Lazy`1[[System.Collections.ObjectModel.ReadOnlyCollection`1[[System.Composition.Hosting.Core.CompositionDependency, System.Composition.Hosting, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a]], System.Private.CoreLib, Version=8.0.0.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e]].get_Value()
   at System.Composition.Hosting.Core.ExportDescriptorPromise.get_Dependencies()
   at System.Composition.Hosting.Core.ExportDescriptorRegistryUpdate.CheckTarget(CompositionDependency , HashSet`1 , Stack`1 )
   at System.Composition.Hosting.Core.ExportDescriptorRegistryUpdate.Execute(CompositionContract )
   at System.Composition.Hosting.Core.ExportDescriptorRegistry.TryGetSingleForExport(CompositionContract , ExportDescriptor& )
   at System.Composition.Hosting.Core.LifetimeContext.TryGetExport(CompositionContract , Object& )
   at System.Composition.Hosting.CompositionHost.TryGetExport(CompositionContract , Object& )
   at System.Composition.CompositionContextExtensions.SatisfyImportsInternal(CompositionContext , Object , AttributedModelProvider )
   at System.Composition.CompositionContextExtensions.SatisfyImports(CompositionContext , Object )
   at Microsoft.CodeAnalysis.Host.Mef.MefHostServices.Microsoft.CodeAnalysis.Host.Mef.IMefHostExportProvider.GetExports[IWorkspaceService,WorkspaceServiceMetadata]()
   at Microsoft.CodeAnalysis.Host.Mef.MefWorkspaceServices..ctor(IMefHostExportProvider , Workspace )
   at Microsoft.CodeAnalysis.Host.Mef.MefHostServices.CreateWorkspaceServices(Workspace )
   at Microsoft.CodeAnalysis.Workspace..ctor(HostServices , String )
   at Microsoft.CodeAnalysis.AdhocWorkspace..ctor(HostServices , String )
   at MonacoRoslynCompletionProvider.CompletionWorkspace.Create()
   at Bootsharp.Generated.Interop.MonacoRoslynCompletionProvider_CompletionWorkspace_Create()
   at Bootsharp.Generated.Interop.__Wrapper_MonacoRoslynCompletionProvider_CompletionWorkspace_Create_1430124879(JSMarshalerArgument* __arguments_buffer)
Error: The type 'WorkspaceServiceMetadata' cannot be used as a metadata view. A metadata view must be a concrete class with a parameterless or dictionary constructor.
elringus commented 7 months ago

I doubt it's caused by Bootsharp itself, but maybe by the project's trimming settings.

Seems like this WorkspaceServiceMetadata type is missing constructor, which may be a result of trimming.

To test this, make sure BootsharpAggressiveTrimming is not enabled and disable all the .NET's trimming options: https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trimming-options

knervous commented 7 months ago

Thanks for the quick reply--BootsharpAggressiveTrimming is definitely off. I tried a few permutations like setting <TrimMode>partial</TrimMode> with and without some rooting definitions

    <TrimmerRootAssembly Include="Microsoft.CodeAnalysis.Workspaces" />
    <TrimmerRootAssembly Include="Microsoft.CodeAnalysis" />
    <TrimmerRootAssembly Include="Microsoft.CodeAnalysis.Features" />
    <TrimmerRootAssembly Include="Microsoft.CodeAnalysis.CSharp" />
    <TrimmerRootAssembly Include="System.Composition.Hosting" />
    <TrimmerRootAssembly Include="System.Composition.TypedParts" />

With these enabled, I encounter an error that happens prior to where I was getting before actually

marshal-to-js.ts:349 Uncaught    at System.Composition.TypedParts.Discovery.TypeInspector.InspectTypeForPart(TypeInfo , DiscoveredPart& )
   at System.Composition.TypedParts.TypedPartExportDescriptorProvider..ctor(IEnumerable`1 , AttributedModelProvider )
   at System.Composition.Hosting.ContainerConfiguration.CreateContainer()
   at Microsoft.CodeAnalysis.Host.Mef.MefHostServices.Create(IEnumerable`1 )
   at MonacoRoslynCompletionProvider.CompletionWorkspace.Create()
   at Bootsharp.Generated.Interop.MonacoRoslynCompletionProvider_CompletionWorkspace_Create()
   at Bootsharp.Generated.Interop.__Wrapper_MonacoRoslynCompletionProvider_CompletionWorkspace_Create_1808268403(JSMarshalerArgument* __arguments_buffer)
Error: VTable setup of type System.Composition.TypedParts.Discovery.TypeInspector+<DiscoverExports>d__5 failed
    at Jn (http://localhost:59933/bin/dotnet.runtime.js:3:31614)
    at kr (http://localhost:59933/bin/dotnet.runtime.js:3:35529)
    at Object.<anonymous> (http://localhost:59933/bin/dotnet.runtime.js:3:180707)
    at Object.create (http://localhost:59933/index.mjs:275:48)
    at http://localhost:59933/:16:71

Are there any particular settings I should go after? I see trim warnings for many of these dll's coming up during compile time: /home/pjohnson/.nuget/packages/system.composition.typedparts/8.0.0/lib/net8.0/System.Composition.TypedParts.dll : warning IL2104: Assembly 'System.Composition.TypedParts' produced trim warnings

elringus commented 7 months ago

This new error doesn't seem to be related with trimming, but with JS marshaling.

Can you share the JS bindings you're using? Probably something there can't be marshaled as-is.

"Assembly 'System.Composition.TypedParts' produced trim warnings" Means that the APIs are not upgraded to support the new .NET trimming model; this should be reported to the maintainers.

knervous commented 7 months ago

No args for the functions, rewrote so there's nothing returning either.

Backend side:

   [JSInvokable]
      public static void CreateNew() {
          Console.WriteLine("Call Create");
          MefHostServices host = MefHostServices.Create(MefHostServices.DefaultAssemblies);
          var workspace = new AdhocWorkspace(host);
          Console.WriteLine("Made ws");
      }

Front end side

<!DOCTYPE html>

<script type="module">

import bootsharp, {MonacoRoslynCompletionProvider} from './index.mjs'

console.log('Boot up');

await bootsharp.boot({ root: "/bin" });
MonacoRoslynCompletionProvider.CompletionWorkspace.getAssemblies = () => bootsharp.resources.assemblies;
await MonacoRoslynCompletionProvider.CompletionWorkspace.initialize();
console.log('Did init')
await MonacoRoslynCompletionProvider.CompletionWorkspace.createNew();
console.log('Did create')

</script>

Here's the stack

Boot up
dotnet.native.js:8 Main invoked
(index):12 Did init
dotnet.native.js:8 Call Create
marshal-to-js.ts:349 Uncaught    at System.Composition.TypedParts.Discovery.TypeInspector.InspectTypeForPart(TypeInfo , DiscoveredPart& )
   at System.Composition.TypedParts.TypedPartExportDescriptorProvider..ctor(IEnumerable`1 , AttributedModelProvider )
   at System.Composition.Hosting.ContainerConfiguration.CreateContainer()
   at Microsoft.CodeAnalysis.Host.Mef.MefHostServices.Create(IEnumerable`1 )
   at MonacoRoslynCompletionProvider.CompletionWorkspace.CreateNew()
   at Bootsharp.Generated.Interop.MonacoRoslynCompletionProvider_CompletionWorkspace_CreateNew()
   at Bootsharp.Generated.Interop.__Wrapper_MonacoRoslynCompletionProvider_CompletionWorkspace_CreateNew_316415403(JSMarshalerArgument* __arguments_buffer)
Error: VTable setup of type System.Composition.TypedParts.Discovery.TypeInspector+<DiscoverExports>d__5 failed
    at Jn (http://localhost:59933/bin/dotnet.runtime.js:3:31614)
    at kr (http://localhost:59933/bin/dotnet.runtime.js:3:35529)
    at Object.<anonymous> (http://localhost:59933/bin/dotnet.runtime.js:3:180707)
    at Object.createNew (http://localhost:59933/index.mjs:275:39)
    at http://localhost:59933/:13:58

elringus commented 7 months ago

This is just a guess (I'm not familiar with monaco/roslyn stuff), but if this:

MonacoRoslynCompletionProvider.CompletionWorkspace.getAssemblies = () => bootsharp.resources.assemblies;

— is an attempt to pass raw WASM-compiled DLLs back to managed C#, this would most likely won't work due to version/asm metadata mismatch.

knervous commented 7 months ago

Yep, hadn't gotten far enough to see if it was actually working to load the bytes back into a metadatareference. FWIW that same pattern works in Blazor by getting the bytes from the assembly and loading them in and passing to Roslyn.

In blazor from an http request to get the static data:

                    var bytes = await tmp.Content.ReadAsByteArrayAsync();
                    ret = MetadataReference.CreateFromImage(bytes);

in bootsharp:

        [JSInvokable]
        public static void Initialize() {
            foreach(var a in GetAssemblies()) {
                try {
                    var bytes = Convert.FromBase64String(a.Content);
                    assemblyBytes.Add(a.Name, bytes);
                    metadataReferences.Add(a.Name, MetadataReference.CreateFromImage(bytes));
                } catch(Exception e) {
                }
            }
        }

        [JSFunction]
        public static partial IReadOnlyList<AssemblyResource> GetAssemblies ();

I'll keep tinkering around with it, sounds like you're onto something with the trimming there so I will keep digging. Appreciate the help

github-actions[bot] commented 7 months ago

This issue is stale because it has been open 14 days with no activity. It will be automatically closed in 7 days.