 Before I was at Fermion, I did a lot of work on the .NET stack of a C-Shark program for 15 years and this talk is kind of combining those two strands of my developer existence. So looking all the way back to the early 1990s, in the beginning there was Visual Basic, a rapid application development environment for 16-bit and later 32-bit windows. It was a monolith that kind of got gradually prized apart but always stayed pretty much tied to its own proprietary runtime. And as things like Java began competing with it, most of the time they wanted to do another language and they figured out that if they were going to do that they should share a runtime because then you can have lots of other languages on those as well. And thus was the common language runtime born, also known because marketing at the time .NET. So initially .NET was released as a as a Windows framework. It was closed source but it was a specification submitted to ECMA and that meant that other implementers could come along. The Mono project implemented .NET for Linux later on and we don't like to talk about this too much. There was Silverlight which was a cross-platform implementation of a subset of .NET to run in the browser and then when Microsoft began embracing open source a bit more there was .NET Core which is now actually the primary development stream of .NET. That is fully cross-platform Linux, Mac, Windows and is a much sort of smaller refocused version of the framework. And then a few years ago Steve Sanderson from Microsoft figured out that they could compile the .NET runtime, compile this execution environment to WebAssembly and run it in the browser and that was called Blazor. So and I want to just shift and give you some context on how this all works. When you write C sharp code or F sharp or VB .NET or whatever and compile that you end up with these blobs of what's called intermediate language CIL common intermediate language bundled into assemblies and those are self-described they got metadata they can talk to each other and together they form an application. That intermediate language is not machine code it's more like Java bytecode and it's run by an execution engine called the virtual execution system and that sits over the host that's the classic .NET model. When we take that to Blazor all we need to do all we need to do is to recompile the execution engine from targeting machine code from x86 instructions to wasm instructions and then you get an execution environment that's able to run these IL instructions in the browser. Now recently Steve Sanderson has added wazzy support via a project called the .NET wazzy SDK an experimental project I should probably stress called the .NET wazzy SDK and this is going to allow .NET applications to run to be compiled to wasm on the server. Now notice once again still we've got the IL blobs the DLLs the assemblies but now we've got a wazzy host such as wasm time instead of a browser and it means that the standard library the base class library can do things like file IO by talking to the wazzy interface rather than having to drop it on the drop it on the floor or fail. The wazzy SDK has one limitation from certainly from Fermion's point of view which is that it's a it's a it's a it's a it's a wazzy start model underscore star which means that the execution comes in the top runs to the bottom and then exits. That's not what we need for the future of the component model and wasm interface types. What we need is something more like this where in addition to the IL blobs talking to each other they can also talk to services that are export they can import services exported by the host and they can provide callable interfaces that the host can enter instead of just the the start point and this is coming back to Luke's worlds concept. The host now is sort of is or implements a world which the dotnet components live in and satisfy the the contracts of. So this opens up additional potential because we can now talk to other components in that same world via their contracts and that means we're not limited to talking dotnet to dotnet or dotnet to host. We can also talk for example dotnet to Java. We can use the work that Joel did to build say some business rules we've got in Java into a wazzy module and then we can have our dotnet application using those business rules via the wasm interface. We don't care that it's Java on the other side, we don't care that it's built from Rust or C or Go or Java or whatever. It's all wasm. It's all wasm interface types. So that's the rainbow dream but there's always a but. So if we look inside the wazzy SDK what we find is that it's got the wazzy SDK hijacks dotnet compilation instead of just putting out a dotnet DLL an IL blob. It actually puts out a it actually adds in some C code which gets compiled to wasm and it's the C code that talks to that loads the dotnet blobs as opposed to the the raw BS. So the wazzy SDK exposes an underscore start or C main method that's compiled to C that method that piece of C code launches the mono implementation of the dotnet CLR and asks it to load the users assembly and call it's main and that's all hardwired into the wazzy SDK. What we want is something more like this where we can have C code that exposes dotnet exports and consume sorry that exports wasm interfaces and consume can import wasm interfaces that are exported by the host. Now within that export entry point we need to launch the VES and that can then launch the user implementation of find the right entry point for that and then those can call back we can map C sharp libraries or C sharp methods onto C methods and those can then call back through the widget that the generated generated import layer. Is that making sense so far? Any questions so far? Alright so what you see here is what can kind of be called Franken code it's a mix of C sharp for the user application the user code and C for those interop layers and I'm now going to see how far I can get with building one of these things live. So what I've got here is some wasm interface files I've got a an export thing so this is going to handle console triggers it's going to handle a line of input and I want to return a line of output well first of all I'll start by creating myself a project so that's just creating a new empty.net project and then I'm going to use the whip bind gen tool that Luke alluded to and I'm going to say I wanted to create exports from wit slash console.wit and I'm going to put them inside that project so what have we out there thank you very much so what have we got coming out from that. We've got this native and we've got this console.c and console.h and in that console.c because it's an export you'll see it's exporting this function from the C code it's going to get compiled down to wasm and it's going to become a wasm export and inside that function does some marshalling and then it calls this function which if we were to look through the C code we will find that that function was not defined anywhere we've got to define that and to do that we will have some custom C code which I will add so what's this doing well you can see there's a bunch of hash includes of the the mono source there so we're loading we're going to be loading the the mono source I'll scroll down to the end here so this is the missing function this is going to get linked by the the clang compiler which is invoked by the wasm SDK this is going to get linked with that export like wit bind gen export code and that means that when our host calls that export we will go through that C marshalling code and we will end up here and what does this do well it does some sorcery does some stuff we prefer not to talk about because that's supposed to be internals but it doesn't work if we don't do that and then it initializes the CLR it loads the runtime it does some stuff called attaching internal calls which don't need yet so we can probably get rid of that and then it tries to find an entry point in the user code now this is where things got a little bit sticky because there's no obvious entry point as there is with main so how are we going to solve that well a traditional common way of doing that in .net is to either implement a marker interface or to use an attribute for the way that I've done it in my project I've used an attribute so I'm going to need to add that declaration to my project as well and for those of you know there's nothing really going on there it's just a mark it's just a way of labeling a method and I'm also going to need the the hairy bit that finds the method which I've not made a snippet for so let's go copy that out from from here and we'll drop that into the native directory this is doing a a whole bunch of effectively reflection but it's doing reflection from the the mono hosting side so it's using mono APIs to inspect the .net assembly and find something that's decorated with that attribute so now I should be able to go in here and write public static string my handler string input and that should get called sorry I need to add a namespace so it all shows up all right now we need to get it to compile all that stuff together because we reference the WASD SDK when you tell the WASD SDK that there are some additional files that we need to bring in and that's handled by this targets file which says I want you to include the console.c I want you to include my console.c and my util.c and I'm also going to need to to include that console export the thing that's actually handling the the entry point and then I will need to reference those in my csproj file so I'll just again copy those from a previous version so I'll need to have those in my project and I will also need to and I'm also going to need to add the package you can tell why I don't like to do this too often all right so that's added the WASD SDK that will take care of handing compilation duties over to to Clang and now we can give this a go and see how it works out and this is kind of calling back to what we heard earlier about wanting to use familiar tools this does work with just .NET build there's no special WASM compilation it's all taken care of by the WASD SDK so let's see what we've see what we missed okay what we've missed is that C strings are not .NET strings so if I look in my console.h the type that's being passed across the boundary is one of these it's a it's a pointer and a length and that's not a .NET string so I need to actually map something that corresponds I need to have something that corresponds to that on the C sharp side so things we passed across correctly and the way I can do that is a creating a struct a C sharp struct that represents that canonical ABI format so you can see there it's got the the pointer and the length and it's marked as sequential layout and what that means is it's telling the C sharp compiler that that struct should adopt the the C ABI it should be laid out in memory according to the C ABI and then there's some help methods on there that can convert it to and from strings so now I can switch back to my application code this needs to return an interrupt string and it needs to take an interrupt string and for the time being I'll do I'll return interrupt string dot from string just save that and we'll try that unsafe code that means that I need to also allow unsafe code to be a thing which means you go back in here and I need to on safe blocks true and we'll try building that again and now the C sharp has built successfully and what it's doing is it's building it's built those DLLs including all the dependency DLLs and it's now trying to link those with the the C code and when it fails it does tend to produce these very long error messages all right this is a little bit of a silly issue that's caused by Whitbine Gen the way that it generates these things it creates the wrong include statement for the .h file so we just need to massage that generated code which I we always hate doing but we've not found a workaround for that yet okay I will switch back to my pre-prepared one thank you for your patience but I was getting a bit over optimistic there so let's switch back to .net guest and .net build that one and this is what you've seen except without the mistakes it's the same same code that you've you've just been looking at so how can I use this well I've got a little host program written in Rust that loads that just loads loads wasm time and calls through that that export so it's just looping reading the line from the console and when it gets one it calls into into it calls for an export so I'm going to change up I will .host target release host and I'll pass it the wasm file that I want to use as a command line argument which is going to be .net guest bin debug net 7.0 wasm day forgot my own namespace now wasm day .net thank you for some reason I'm not getting completion on that argument so Rust the Rust host is now waiting for command and I will give it a command of joke and that's now calling into my application here so we've received a line I've matched the line on joke and that is now calling out via a an import the random thing import so I'm just constructing a and again this is one of these interrupt structures and then I'm passing that to this call what's that that's got this internal call declaration on that means it's a call that is implemented by the runtime itself by the by the execution system itself which and this is how we achieve imports because if I go and look for where that's done I can see that that internal call is attached to is attached to a C function and that C function has been generated by another with bind gen that's in here and that with bind gen then backs out onto a service is implemented in the world of the Rust host all right does that make sense I'm sorry that's been a little bit I've shown that in a slightly confusing way does it does it make sense people how that's hanging together that we've got the the C generated code forming the interface to the to the host to the world exporting and importing things this binding layer of C interrupt stuff like the interrupt string and the internal call melding that into the C sharp net world and then the application being able to handle those callbacks and call out through the intro yes can I come back to great question can I come back to so the question was about can we generate the bindings in C sharp and I will come back to that in just a moment so thank you any other questions around that so let's talk about challenges where we out with this well one challenge that we hit quite hard when we're working with this for spin was start-up time the the dot net design is is aimed at amortizing start of time you you expect to be a long running process it takes a while to spin up the runtime maybe sort of 30 40 milliseconds something like that but then each call should be quite quick but in a in a wasm environment in a world-like environment where your export is being called quite frequently you don't have there is it's wasteful to spin dot net up every time the solution that we have for that is to use a tool called wiser and we can and if you look in the spin on a snapshot put a link to wiser allows us to run the module to a certain point so what we can do is we can in our wiser function we can start the runtime load the assemblies inject a fake request to warm up the actual handler and then capture the state of the module at that point and that means that every request that goes to that captured version of the module is going into something that's already pre-warmed and ready to go so it's not a great work around and there are some weird side effects which are documented in the spin SDK but it it certainly helps a lot in a lot of cases stability is a problem that I've hit to the point of banging my head against the desk a few times we're doing some kind of fairly hairy sea magic here in a experimental environment and I've certainly hit a few kind of mismatch signature which I just done out a lot of those do seem to be down to wiser but again not fully understood yet size is a bit of a problem at the moment the the generated binaries are about 20 megs for a hello world which is not out of proportion for some of those big runtimes but it does allocate a whole lot of memory whole lot of was millennia memory which we've had to work around future versions of the WASD SDK should improve that and the one that you so rightly identified convenience you do not you do not want to go through what I just went through on a regular basis even if you know what you're doing the the the process of generating the sea files setting up all the linkage linkages writing those interop bridges and writing the the gunky stuff on the sea sea sharp side is a mess where we would like to be is to be able to generate those sea sharp bindings directly which would be a whip bind gen back end and then somehow from there generate the sea bridging or the the equivalent ABI level bridging stuff which might be doable within dot net or we might be able to we might need to use some like Roslyn source generators to spit out that sea code and make sure it gets linked in so some definite tooling improvements that absolutely need to be done before this can be production ready and with that I'll say don't play with it we've got an SDK for work for working with net with sea sharp in spin you can just add it as a new get package and use it in spin today but I hope what I've shown you is also relevant even if you're not interested in in working with net I hope that this is illustrated a technique that you could be able to use for other languages for supporting those worlds and interfaces in other languages that don't compile down directly to the wasm that live inside these runtimes and without leaving some links the the working code is in that top repo the wasm day twenty two one the spin dot net one which illustrates a more real world use case and illustrates the wisering technique is in spin on SDK and I need to acknowledge the foundational work that Steve Sanderson has done with the was the SDK thank you very much any questions for Ivan all right thank you so much Ivan appreciate it all right our next two speakers are no strangers to open source by any means Daniel is a serial entrepreneur in his own right and was the past founder of bitnami and Raphael among as many other things they're both now in the VMware office of the CTO worked on KDE for like forever right like 16 years long time so blame him for all the Linux presentation issues not working today I think