diff --git a/crates/misc/dotnet/.gitignore b/crates/misc/dotnet/.gitignore
new file mode 100644
index 0000000000..1944291481
--- /dev/null
+++ b/crates/misc/dotnet/.gitignore
@@ -0,0 +1,8 @@
+*.swp
+*.*~
+.DS_Store
+
+.vscode
+
+bin/
+obj/
diff --git a/crates/misc/dotnet/Directory.Build.props b/crates/misc/dotnet/Directory.Build.props
new file mode 100644
index 0000000000..b652414ca0
--- /dev/null
+++ b/crates/misc/dotnet/Directory.Build.props
@@ -0,0 +1,6 @@
+
+
+ 0.8.0
+ wasmtime
+
+
diff --git a/crates/misc/dotnet/Directory.Build.targets b/crates/misc/dotnet/Directory.Build.targets
new file mode 100644
index 0000000000..fd577ba0c8
--- /dev/null
+++ b/crates/misc/dotnet/Directory.Build.targets
@@ -0,0 +1,12 @@
+
+
+ lib
+ .dll
+ .dylib
+ .so
+ $(LibraryPrefix)$(WasmtimeLibraryName)$(LibraryExtension)
+ $(MSBuildThisFileDirectory)../../../target/$(Configuration.ToLower())
+ cargo build --release -p $(WasmtimeLibraryName)
+ cargo build -p $(WasmtimeLibraryName)
+
+
diff --git a/crates/misc/dotnet/LICENSE b/crates/misc/dotnet/LICENSE
new file mode 100644
index 0000000000..261eeb9e9f
--- /dev/null
+++ b/crates/misc/dotnet/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/crates/misc/dotnet/README.md b/crates/misc/dotnet/README.md
new file mode 100644
index 0000000000..5806934641
--- /dev/null
+++ b/crates/misc/dotnet/README.md
@@ -0,0 +1,108 @@
+# Wasmtime for .NET
+
+A .NET API for [Wasmtime](https://github.com/bytecodealliance/wasmtime).
+
+Wasmtime for .NET enables .NET code to instantiate WebAssembly modules and to interact with them in-process.
+
+# Getting Started
+
+## Prerequisites
+
+### .NET Core 3.0
+
+Install a [.NET Core 3.0+ SDK](https://dotnet.microsoft.com/download) for your operating system.
+
+## Introduction to Wasmtime for .NET
+
+See the [introduction to Wasmtime for .NET](https://peterhuene.github.io/wasmtime.net/articles/intro.html) for a complete walkthrough of how to use Wasmtime for .NET.
+
+# Wasmtime for .NET API documentation
+
+See the [Wasmtime for .NET API documentation](https://peterhuene.github.io/wasmtime.net/api/index.html) for documentation on using the Wasmtime for .NET types.
+
+# Running the "Hello World" Example
+
+The "hello world" example demonstrates a simple C# function being called from WebAssembly.
+
+To run the "hello world" example, follow these instructions:
+
+1. `cd examples/hello`
+2. `dotnet run`
+
+You should see a `Hello from C#, WebAssembly!` message printed.
+
+# Building Wasmtime for .NET
+
+To build Wasmtime for .NET, follow these instructions:
+
+1. `cd src`.
+2. `dotnet build`.
+
+This should produce a `Wasmtime.Dotnet.dll` assembly in the `bin/Debug/netstandard2.1` directory.
+
+To build a release version of Wasmtime for .NET, follow these instructions:
+
+1. `cd src`.
+2. `dotnet build -c Release`.
+
+This should produce a `Wasmtime.Dotnet.dll` assembly in the `bin/Release/netstandard2.1` directory.
+
+# Running the tests
+
+To run the Wasmtime for .NET unit tests, follow these instructions:
+
+1. `cd tests`.
+2. `dotnet test`.
+
+# Packing Wasmtime for .NET
+
+To create a NuGet package for Wasmtime for .NET, follow these instructions:
+
+1. `cd src`.
+2. `dotnet pack -c Release`.
+
+This should produce a `Wasmtime..nupkg` file in the `bin/Release` directory.
+
+# Implementation Status
+
+## Status
+
+| Feature | Status |
+|---------------------------------------|--------|
+| Wasmtime engine class | ✅ |
+| Wasmtime store class | ✅ |
+| Wasmtime module class | ✅ |
+| Wasmtime instance class | 🔄 |
+| Module function imports | ✅ |
+| Module global imports | ✅ |
+| Module table imports | ✅ |
+| Module memory imports | ✅ |
+| Module function exports | ✅ |
+| Module global exports | ✅ |
+| Module table exports | ✅ |
+| Module memory exports | ✅ |
+| Extern instance functions | ✅ |
+| Extern instance globals | ✅️ |
+| Extern instance tables | ⬜️ |
+| Extern instance memories | ✅️ |
+| Host function import binding | ✅ |
+| Host global import binding | ✅ ️️ |
+| Host table import binding | ⬜️ ️️ |
+| Host memory import binding | ✅️ ️️ |
+| `i32` parameters and return values | ✅ |
+| `i64` parameters and return values | ✅ |
+| `f32` parameters and return values | ✅ |
+| `f64` parameters and return values | ✅ |
+| `AnyRef` parameters and return values | ⬜️ |
+| Tuple return types for host functions | ✅ |
+| Trap messages | ✅ |
+| Trap frames | ⬜️ |
+| Create a NuGet package | ✅ |
+
+## Legend
+
+| Status | Icon |
+|-----------------|--------|
+| Not implemented | ⬜️ |
+| In progress | 🔄 |
+| Completed | ✅ |
diff --git a/crates/misc/dotnet/docs/.gitignore b/crates/misc/dotnet/docs/.gitignore
new file mode 100644
index 0000000000..2781f6d586
--- /dev/null
+++ b/crates/misc/dotnet/docs/.gitignore
@@ -0,0 +1,9 @@
+###############
+# folder #
+###############
+/**/DROP/
+/**/TEMP/
+/**/packages/
+/**/bin/
+/**/obj/
+_site
diff --git a/crates/misc/dotnet/docs/api/.gitignore b/crates/misc/dotnet/docs/api/.gitignore
new file mode 100644
index 0000000000..f798527e61
--- /dev/null
+++ b/crates/misc/dotnet/docs/api/.gitignore
@@ -0,0 +1,5 @@
+###############
+# temp file #
+###############
+*.yml
+.manifest
diff --git a/crates/misc/dotnet/docs/api/index.md b/crates/misc/dotnet/docs/api/index.md
new file mode 100644
index 0000000000..74ae3a863a
--- /dev/null
+++ b/crates/misc/dotnet/docs/api/index.md
@@ -0,0 +1,5 @@
+# Wasmtime for NET
+
+This is the .NET API for [Wasmtime](https://github.com/bytecodealliance/wasmtime).
+
+See the [documentation](Wasmtime.html#classes) for the various .NET classes.
\ No newline at end of file
diff --git a/crates/misc/dotnet/docs/articles/intro.md b/crates/misc/dotnet/docs/articles/intro.md
new file mode 100644
index 0000000000..6b1d2b9552
--- /dev/null
+++ b/crates/misc/dotnet/docs/articles/intro.md
@@ -0,0 +1,214 @@
+# Introduction to Wasmtime for .NET
+
+[Wasmtime](https://github.com/bytecodealliance/wasmtime) is a standalone runtime capable of executing [WebAssembly](https://webassembly.org/) outside of a web browser.
+
+Wasmtime for .NET is a .NET API for Wasmtime. It enables .NET developers to easily instantiate and execute WebAssembly modules.
+
+For this tutorial, we will create a WebAssembly module from a program written in Rust and use that WebAssembly module from a .NET Core 3.0 application.
+
+# Creating a simple WebAssembly module
+
+One of the reasons why WebAssembly is so exciting is that [many languages are able to target WebAssembly](https://github.com/appcypher/awesome-wasm-langs). This means, for example, a plugin model based on WebAssembly could enable developers to write sandboxed, cross-platform plugins in any number of languages.
+
+Here I've decided to use [Rust](https://www.rust-lang.org/) for the implementation of the WebAssembly module. Rust is a modern systems programming language that can easily target WebAssembly.
+
+If you wish to skip creating the WebAssembly module, download the [prebuilt WebAssembly module](https://raw.githubusercontent.com/bytecodealliance/wasmtime/master/crates/misc/dotnet/docs/wasm/intro/hello.wasm) from this tutorial, copy it to your .NET project directory, and continue from the _[Using the WebAssembly module from .NET](#using-the-webassembly-module-from-net)_ section.
+
+## Installing a Rust toolchain
+
+To get started with Rust, install [rustup](https://rustup.rs/), the manager for Rust toolchains.
+
+This will install both a `rustup` command and a `cargo` command (for the active Rust toolchain) to your PATH.
+
+## Installing the WebAssembly target
+
+To target WebAssembly with the active Rust toolchain, install the WebAssembly [target triple](https://forge.rust-lang.org/release/platform-support.html):
+
+```text
+rustup target add wasm32-unknown-unknown
+```
+
+## Creating the Rust project
+
+Create a new Rust library project named `hello`:
+
+```text
+cargo new --lib hello
+cd hello
+```
+
+To target WebAssembly, the library needs to be built as a `cdylib` (dynamic library) rather than the default of a static Rust library. Add the following to the `Cargo.toml` file in the project root:
+
+```toml
+[lib]
+crate-type = ["cdylib"]
+```
+
+## Implementing the WebAssembly code
+
+The WebAssembly implementation will import a `print` function from the host environment and pass it a string to print. It will export a `run` function that will invoke the imported `print` function.
+
+Replace the code in `src/lib.rs` with the following Rust code:
+
+```rust
+extern "C" {
+ fn print(address: i32, length: i32);
+}
+
+#[no_mangle]
+pub unsafe extern fn run() {
+ let message = "Hello world!";
+ print(message.as_ptr() as i32, message.len() as i32);
+}
+```
+
+Note that this example passes the string as a pair of _address and length_. This is because WebAssembly only supports a few core types (such as integers and floats) and a "string" has no native representation in WebAssembly.
+
+In the future, WebAssembly will support [interface types](https://hacks.mozilla.org/2019/08/webassembly-interface-types/) that will enable higher-level abstractions of types like strings so they can be represented in a natural way.
+
+Also note that the _address_ is not actually a physical memory address within the address space of a process but an address within the _[WebAssembly memory](https://hacks.mozilla.org/2017/07/memory-in-webassembly-and-why-its-safer-than-you-think/)_ of the module. Thus the WebAssembly module has no direct access to the memory of the host environment.
+
+## Building the WebAssembly module
+
+Use `cargo build` to build the WebAssembly module:
+
+```text
+cargo build --target wasm32-unknown-unknown --release
+```
+
+This should create a `hello.wasm` file in the `target/wasm32-unknown-unknown/release` directory. We will use `hello.wasm` in the next section of the tutorial.
+
+As this example is very simple and does not require any of the data from the custom sections of the WebAssembly module, you may use `wasm-strip` if you have the [WebAssembly Binary Toolkit](https://github.com/WebAssembly/wabt) installed:
+
+```text
+wasm-strip target/wasm32-unknown-unknown/release/hello.wasm
+```
+
+The resulting file should be less than 200 bytes.
+
+# Using the WebAssembly module from .NET
+
+## Installing a .NET Core 3.0 SDK
+
+Install a [.NET Core 3.0 SDK](https://dotnet.microsoft.com/download/dotnet-core/3.0) for your platform if you haven't already.
+
+This will add a `dotnet` command to your PATH.
+
+## Creating the .NET Core project
+
+The .NET program will be a simple console application, so create a new console project with `dotnet new`:
+
+```text
+mkdir tutorial
+cd tutorial
+dotnet new console
+```
+
+## Referencing the Wasmtime for .NET package
+
+To use Wasmtime for .NET from the project, we need to add a reference to the [Wasmtime NuGet package](https://www.nuget.org/packages/Wasmtime):
+
+```text
+dotnet add package --version 0.0.1-alpha1 wasmtime
+```
+
+_Note that the `--version` option is required because the package is currently prerelease._
+
+This will add a `PackageReference` to the project file so that Wasmtime for .NET can be used.
+
+## Implementing the .NET code
+
+Replace the contents of `Program.cs` with the following:
+
+```c#
+using System;
+using Wasmtime;
+
+namespace Tutorial
+{
+ class Host : IHost
+ {
+ public Instance Instance { get; set; }
+
+ [Import("print", Module="env")]
+ public void Print(int address, int length)
+ {
+ var message = Instance.Externs.Memories[0].ReadString(address, length);
+ Console.WriteLine(message);
+ }
+ }
+
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ using (var engine = new Engine())
+ using (var store = engine.CreateStore())
+ using (var module = store.CreateModule("hello.wasm"))
+ using (dynamic instance = module.Instantiate(new Host()))
+ {
+ instance.run();
+ }
+ }
+ }
+}
+```
+
+The `Host` class is responsible for implementing the imported [functions](https://webassembly.github.io/spec/core/syntax/modules.html#functions), [globals](https://webassembly.github.io/spec/core/syntax/modules.html#globals), [memories](https://webassembly.github.io/spec/core/syntax/modules.html#memories), and [tables](https://webassembly.github.io/spec/core/syntax/modules.html#syntax-table) for the WebAssembly module. For Wasmtime for .NET, this is done via the [`Import`](https://peterhuene.github.io/wasmtime.net/api/Wasmtime.ImportAttribute.html) attribute applied to functions and fields of type [`Global`](https://peterhuene.github.io/wasmtime.net/api/Wasmtime.Global-1.html), [`MutableGlobal`](https://peterhuene.github.io/wasmtime.net/api/Wasmtime.MutableGlobal-1.html), and [`Memory`](https://peterhuene.github.io/wasmtime.net/api/Wasmtime.Memory.html) (support for WebAssembly tables is not yet implemented). The [`Instance`](https://peterhuene.github.io/wasmtime.net/api/Wasmtime.IHost.html#Wasmtime_IHost_Instance) property of the host is set during instantiation of the WebAssembly module.
+
+Here the host is implementing an import of `print` in the `env` module, which is the default import module name for WebAssembly modules compiled using the Rust toolchain.
+
+The [`Engine`](https://peterhuene.github.io/wasmtime.net/api/Wasmtime.Engine.html) is used to create a [`Store`](https://peterhuene.github.io/wasmtime.net/api/Wasmtime.Store.html) that will store all Wasmtime runtime objects, such as WebAssembly modules and their instantiations.
+
+A WebAssembly module _instantiation_ is the stateful representation of a module that can be executed. Here, the code is casting the [`Instance`](https://peterhuene.github.io/wasmtime.net/api/Wasmtime.Instance.html) to [`dynamic`](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/types/using-type-dynamic) which allows us to easily invoke the `run` function that was exported by the WebAssembly module.
+
+Alternatively, the `run` function could be invoked without using the runtime binding of the `dynamic` feature like this:
+
+```c#
+...
+using (var instance = module.Instantiate(new Host()))
+{
+ instance.Externs.Functions[0].Invoke();
+}
+...
+```
+
+## Building the .NET application
+
+Use `dotnet build` to build the .NET application:
+
+```text
+dotnet build
+```
+
+This will create a `tutorial.dll` in the `bin/Debug/netcoreapp3.0` directory that implements the .NET Core application. An executable `tutorial` (or `tutorial.exe` on Windows) should also be present in the same directory to run the application.
+
+## Running the .NET application
+
+Before running the application, we need to copy the `hello.wasm` file to the project directory.
+
+Once the WebAssembly module is present in project directory, we can run the application:
+
+```text
+dotnet run
+```
+
+Alternatively, we can execute the program directly without building the application again:
+
+```text
+bin/Debug/netcoreapp3.0/tutorial
+```
+
+This should result in the following output:
+
+```text
+Hello world!
+```
+
+# Wrapping up
+
+We did it! We executed a function written in Rust from .NET and a function implemented in .NET from Rust without much trouble at all. And, thanks to the design of WebAssembly, the Rust code was effectively sandboxed from accessing the memory of the .NET application.
+
+Hopefully this introduction to Wasmtime for .NET has offered a small glipse of the potential of using WebAssembly from .NET.
+
+One last note: _Wasmtime for .NET is currently in a very early stage of development and the API might change dramatically in the future_.
\ No newline at end of file
diff --git a/crates/misc/dotnet/docs/articles/toc.yml b/crates/misc/dotnet/docs/articles/toc.yml
new file mode 100644
index 0000000000..cc503c4cc2
--- /dev/null
+++ b/crates/misc/dotnet/docs/articles/toc.yml
@@ -0,0 +1,2 @@
+- name: Introduction to Wasmtime for .NET
+ href: intro.md
diff --git a/crates/misc/dotnet/docs/docfx.json b/crates/misc/dotnet/docs/docfx.json
new file mode 100644
index 0000000000..baabba0940
--- /dev/null
+++ b/crates/misc/dotnet/docs/docfx.json
@@ -0,0 +1,72 @@
+{
+ "metadata": [
+ {
+ "src": [
+ {
+ "src": "..",
+ "files": [
+ "src/**.csproj"
+ ]
+ }
+ ],
+ "dest": "api",
+ "disableGitFeatures": false,
+ "disableDefaultFilter": false
+ }
+ ],
+ "build": {
+ "content": [
+ {
+ "files": [
+ "api/**.yml",
+ "api/index.md"
+ ]
+ },
+ {
+ "files": [
+ "articles/**.md",
+ "articles/**/toc.yml",
+ "toc.yml",
+ "*.md"
+ ]
+ }
+ ],
+ "resource": [
+ {
+ "files": [
+ "images/**"
+ ]
+ }
+ ],
+ "overwrite": [
+ {
+ "files": [
+ "apidoc/**.md"
+ ],
+ "exclude": [
+ "obj/**",
+ "_site/**"
+ ]
+ }
+ ],
+ "dest": "_site",
+ "globalMetadataFiles": [],
+ "fileMetadataFiles": [],
+ "template": [
+ "default",
+ "templates/darkfx"
+ ],
+ "postProcessors": [],
+ "markdownEngineName": "markdig",
+ "noLangKeyword": false,
+ "keepFileLink": false,
+ "cleanupCacheHistory": false,
+ "disableGitFeatures": false,
+ "globalMetadata": {
+ "_gitContribute": {
+ "repo": "https://github.com/bytecodealliance/wasmtime",
+ "branch": "master"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/crates/misc/dotnet/docs/index.md b/crates/misc/dotnet/docs/index.md
new file mode 100644
index 0000000000..fcb2408085
--- /dev/null
+++ b/crates/misc/dotnet/docs/index.md
@@ -0,0 +1,7 @@
+# Wasmtime for .NET
+
+A .NET API for [Wasmtime](https://github.com/bytecodealliance/wasmtime).
+
+Wasmtime is a standalone runtime for [WebAssembly](https://webassembly.org/), using the Cranelift JIT compiler.
+
+Wasmtime for .NET enables .NET code to instantiate WebAssembly modules and to interact with them in-process.
diff --git a/crates/misc/dotnet/docs/templates/darkfx/partials/head.tmpl.partial b/crates/misc/dotnet/docs/templates/darkfx/partials/head.tmpl.partial
new file mode 100755
index 0000000000..0e43511ace
--- /dev/null
+++ b/crates/misc/dotnet/docs/templates/darkfx/partials/head.tmpl.partial
@@ -0,0 +1,21 @@
+{{!Copyright (c) Oscar Vasquez. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
+
+
+
+
+ {{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}
+
+
+
+ {{#_description}}{{/_description}}
+
+
+
+
+
+
+
+ {{#_noindex}}{{/_noindex}}
+ {{#_enableSearch}}{{/_enableSearch}}
+ {{#_enableNewTab}}{{/_enableNewTab}}
+
\ No newline at end of file
diff --git a/crates/misc/dotnet/docs/templates/darkfx/styles/main.css b/crates/misc/dotnet/docs/templates/darkfx/styles/main.css
new file mode 100755
index 0000000000..173e6ab304
--- /dev/null
+++ b/crates/misc/dotnet/docs/templates/darkfx/styles/main.css
@@ -0,0 +1,394 @@
+body {
+ color: #ccd5dc;
+ font-family: "Open Sans",sans-serif;
+ line-height: 1.5;
+ font-size: 14px;
+ -ms-text-size-adjust: 100%;
+ -webkit-text-size-adjust: 100%;
+ word-wrap: break-word;
+ background-color: #2d2d30;
+}
+
+h1 {
+ font-weight: 600;
+ font-size: 32px;
+}
+
+h2 {
+ font-weight: 600;
+ font-size: 24px;
+ line-height: 1.8;
+}
+
+h3 {
+ font-weight: 600;
+ font-size: 20px;
+ line-height: 1.8;
+}
+
+h5 {
+ font-size: 14px;
+ padding: 10px 0px;
+}
+
+article h1,
+article h2,
+article h3,
+article h4 {
+ margin-top: 35px;
+ margin-bottom: 15px;
+}
+
+article h4 {
+ padding-bottom: 8px;
+ border-bottom: 2px solid #ddd;
+}
+
+.navbar-brand>img {
+ color: #2d2d30;
+}
+
+.navbar {
+ border: none;
+}
+
+.subnav {
+ border-top: 1px solid #ddd;
+ background-color: #333337;
+}
+
+.navbar-inverse {
+ background-color: #1e1e1e;
+ z-index: 100;
+}
+
+.navbar-inverse .navbar-nav>li>a,
+.navbar-inverse .navbar-text {
+ color: #66666d;
+ background-color: #1e1e1e;
+ border-bottom: 3px solid transparent;
+ padding-bottom: 12px;
+}
+
+.navbar-inverse .navbar-nav>li>a:focus,
+.navbar-inverse .navbar-nav>li>a:hover {
+ color: #c5c5de;
+ background-color: #1e1e1e;
+ border-bottom: 3px solid #333337;
+ transition: all ease 0.25s;
+}
+
+.navbar-inverse .navbar-nav>.active>a,
+.navbar-inverse .navbar-nav>.active>a:focus,
+.navbar-inverse .navbar-nav>.active>a:hover {
+ color: #c5c5de;
+ background-color: #1e1e1e;
+ border-bottom: 3px solid #333337;
+ transition: all ease 0.25s;
+}
+
+.navbar-form .form-control {
+ border: none;
+ border-radius: 0;
+}
+
+.toc .level1>li {
+ font-weight: 400;
+}
+
+.toc .nav>li>a {
+ color: #ccd5dc;
+}
+
+.sidefilter {
+ background-color: #2d2d30;
+ border-left: none;
+ border-right: none;
+}
+
+.sidefilter {
+ background-color: #2d2d30;
+ border-left: none;
+ border-right: none;
+}
+
+.toc-filter {
+ padding: 10px;
+ margin: 0;
+ background-color: #2d2d30;
+}
+
+.toc-filter>input {
+ border: none;
+ border-radius: unset;
+ background-color: #333337;
+ padding: 5px 0 5px 20px;
+ font-size: 90%
+}
+
+.toc-filter>input:focus {
+ color: #ccd5dc;
+ transition: all ease 0.25s;
+}
+
+.toc-filter>.filter-icon {
+ display: none;
+}
+
+.sidetoc>.toc {
+ background-color: #2d2d30;
+ overflow-x: hidden;
+}
+
+.sidetoc {
+ background-color: #2d2d30;
+ border: none;
+}
+
+.alert {
+ background-color: inherit;
+ border: none;
+ padding: 10px 0;
+ border-radius: 0;
+}
+
+.alert>p {
+ margin-bottom: 0;
+ padding: 5px 10px;
+ border-bottom: 1px solid;
+ background-color: #212123;
+}
+
+.alert>h5 {
+ padding: 10px 15px;
+ margin-top: 0;
+ margin-bottom: 0;
+ text-transform: uppercase;
+ font-weight: bold;
+ border-top: 2px solid;
+ background-color: #212123;
+ border-radius: none;
+}
+
+.alert>ul {
+ margin-bottom: 0;
+ padding: 5px 40px;
+}
+
+.alert-info{
+ color: #1976d2;
+}
+
+.alert-warning{
+ color: #f57f17;
+}
+
+.alert-danger{
+ color: #d32f2f;
+}
+
+pre {
+ padding: 9.5px;
+ margin: 0 0 10px;
+ font-size: 13px;
+ word-break: break-all;
+ word-wrap: break-word;
+ background-color: #1e1e1e;;
+ border-radius: 0;
+ border: none;
+}
+
+code{
+ background: #1e1e1e !important;
+ border-radius: 2px;
+}
+
+.hljs{
+ color: #bbb;
+}
+
+.toc .nav > li.active > .expand-stub::before, .toc .nav > li.in > .expand-stub::before, .toc .nav > li.in.active > .expand-stub::before, .toc .nav > li.filtered > .expand-stub::before {
+ content: "▾";
+}
+
+.toc .nav > li > .expand-stub::before, .toc .nav > li.active > .expand-stub::before {
+ content: "▸";
+}
+
+.affix ul ul > li > a:before {
+ content: "|";
+}
+
+.breadcrumb .label.label-primary {
+ background: #444;
+ border-radius: 0;
+ font-weight: normal;
+ font-size: 100%;
+}
+
+#breadcrumb .breadcrumb>li a {
+ border-radius: 0;
+ font-weight: normal;
+ font-size: 85%;
+ display: inline;
+ padding: 0 .6em 0;
+ line-height: 1;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: baseline;
+ color: #999;
+}
+#breadcrumb .breadcrumb>li a:hover{
+ color: #c5c5de;
+ transition: all ease 0.25s;
+}
+
+.breadcrumb > li + li:before {
+ content: "⯈";
+ font-size: 75%;
+ color: #1e1e1e;
+ padding: 0;
+}
+
+.toc .level1>li {
+ font-weight: 600;
+ font-size: 130%;
+ padding-left: 5px;
+}
+
+.footer {
+ border-top: none;
+ background-color: #1e1e1e;
+ padding: 15px 0;
+ font-size: 90%;
+}
+
+.toc .nav > li > a:hover, .toc .nav > li > a:focus {
+ color: #fff;
+ transition: all ease 0.1s;
+}
+
+.form-control {
+ background-color: #333337;
+ border: none;
+ border-radius: 0;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+
+.form-control:focus {
+ border-color: #66afe9;
+ outline: 0;
+ -webkit-box-shadow: none;
+ box-shadow: none;
+}
+
+input#search-query:focus {
+ color: #c5c5de;
+}
+
+.table-bordered, .table-bordered>tbody>tr>td, .table-bordered>tbody>tr>th, .table-bordered>tfoot>tr>td, .table-bordered>tfoot>tr>th, .table-bordered>thead>tr>td, .table-bordered>thead>tr>th {
+ border: 1px solid #1E1E1E;
+}
+
+.table-striped>tbody>tr:nth-of-type(odd) {
+ background-color: #212123;
+}
+
+blockquote {
+ padding: 10px 20px;
+ margin: 0 0 10px;
+ font-size: 110%;
+ border-left: 5px solid #69696e;
+ color: #69696e;
+}
+
+.pagination>.disabled>a, .pagination>.disabled>a:focus, .pagination>.disabled>a:hover, .pagination>.disabled>span, .pagination>.disabled>span:focus, .pagination>.disabled>span:hover {
+ background-color: #333337;
+ border-color: #333337;
+}
+
+.breadcrumb>li, .pagination {
+ display: inline;
+}
+
+@media (min-width: 1600px){
+ .container {
+ width: 100%;
+ }
+ .sidefilter {
+ width: 20%;
+ }
+ .sidetoc{
+ width: 20%;
+ }
+ .article.grid-right {
+ margin-left: 21%;
+ }
+}
+
+code {
+ color:#8ec4f5;
+}
+
+.lang-text {
+ color:#ccd5dc;
+}
+
+button, a {
+ color: #8ec4f5;
+}
+
+.affix > ul > li.active > a, .affix > ul > li.active > a:before {
+ color: #8ec4f5;
+}
+
+.affix ul > li.active > a, .affix ul > li.active > a:before {
+ color: #8ec4f5;
+}
+
+.affix ul > li > a {
+ color: #d3cfcf;
+}
+
+button:hover, button:focus, a:hover, a:focus {
+ color: #339eff;
+}
+
+.toc .nav > li.active > a {
+ color: #8ec4f5;
+}
+
+.toc .nav > li.active > a:hover, .toc .nav > li.active > a:focus {
+ color: #339eff;
+}
+
+.navbar-inverse .navbar-nav > li > a, .navbar-inverse .navbar-text {
+ color: #9898a6;
+}
+
+.navbar-inverse .navbar-nav>.active>a,
+.navbar-inverse .navbar-nav>.active>a:focus,
+.navbar-inverse .navbar-nav>.active>a:hover {
+ color: #fff;
+}
+
+.hljs-attr,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-title {
+ color:#b685ff;
+}
+
+.hljs-keyword,.hljs-selector-tag,.hljs-type {
+ color:#cc74a6;
+}
+
+.hljs-emphasis,.hljs-quote,.hljs-string,.hljs-strong,.hljs-template-variable,.hljs-variable {
+ color:#cc74a6
+}
+
+#breadcrumb .breadcrumb >li a {
+ color: #999;
+}
+
+.toc-filter > input {
+ color: #d3cfcf;
+}
\ No newline at end of file
diff --git a/crates/misc/dotnet/docs/toc.yml b/crates/misc/dotnet/docs/toc.yml
new file mode 100644
index 0000000000..067dee7d4d
--- /dev/null
+++ b/crates/misc/dotnet/docs/toc.yml
@@ -0,0 +1,7 @@
+- name: Articles
+ href: articles/
+- name: API Documentation
+ href: api/
+ homepage: api/index.md
+- name: GitHub Repo
+ href: https://github.com/bytecodealliance/wasmtime
diff --git a/crates/misc/dotnet/docs/wasm/intro/hello.wasm b/crates/misc/dotnet/docs/wasm/intro/hello.wasm
new file mode 100755
index 0000000000..9262fdacaa
Binary files /dev/null and b/crates/misc/dotnet/docs/wasm/intro/hello.wasm differ
diff --git a/crates/misc/dotnet/examples/global/Program.cs b/crates/misc/dotnet/examples/global/Program.cs
new file mode 100644
index 0000000000..4dcf32b131
--- /dev/null
+++ b/crates/misc/dotnet/examples/global/Program.cs
@@ -0,0 +1,33 @@
+using System;
+using Wasmtime;
+
+namespace HelloExample
+{
+ class Host : IHost
+ {
+ public Instance Instance { get; set; }
+
+ [Import("print_global")]
+ public void PrintGlobal()
+ {
+ Console.WriteLine($"The value of the global is: {Global.Value}.");
+ }
+
+ [Import("global")]
+ public readonly MutableGlobal Global = new MutableGlobal(1);
+ }
+
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ using (var engine = new Engine())
+ using (var store = engine.CreateStore())
+ using (var module = store.CreateModule("global.wasm"))
+ using (dynamic instance = module.Instantiate(new Host()))
+ {
+ instance.run(20);
+ }
+ }
+ }
+}
diff --git a/crates/misc/dotnet/examples/global/global.csproj b/crates/misc/dotnet/examples/global/global.csproj
new file mode 100644
index 0000000000..0ac0a26c74
--- /dev/null
+++ b/crates/misc/dotnet/examples/global/global.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ netcoreapp3.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/crates/misc/dotnet/examples/global/global.wasm b/crates/misc/dotnet/examples/global/global.wasm
new file mode 100644
index 0000000000..eb76274f5b
Binary files /dev/null and b/crates/misc/dotnet/examples/global/global.wasm differ
diff --git a/crates/misc/dotnet/examples/global/global.wat b/crates/misc/dotnet/examples/global/global.wat
new file mode 100644
index 0000000000..22e93a9c9c
--- /dev/null
+++ b/crates/misc/dotnet/examples/global/global.wat
@@ -0,0 +1,22 @@
+(module
+ (import "" "print_global" (func $.print_global))
+ (import "" "global" (global $.global (mut i32)))
+ (func $run (param i32) (local $i i32)
+ loop $l1
+ call $.print_global
+ global.get $.global
+ i32.const 2
+ i32.mul
+ global.set $.global
+ local.get $i
+ i32.const 1
+ i32.add
+ local.set $i
+ local.get $i
+ local.get 0
+ i32.le_u
+ br_if $l1
+ end
+ )
+ (export "run" (func $run))
+)
diff --git a/crates/misc/dotnet/examples/hello/Program.cs b/crates/misc/dotnet/examples/hello/Program.cs
new file mode 100644
index 0000000000..c4d853386b
--- /dev/null
+++ b/crates/misc/dotnet/examples/hello/Program.cs
@@ -0,0 +1,30 @@
+using System;
+using Wasmtime;
+
+namespace HelloExample
+{
+ class Host : IHost
+ {
+ public Instance Instance { get; set; }
+
+ [Import("hello")]
+ public void SayHello()
+ {
+ Console.WriteLine("Hello from C#, WebAssembly!");
+ }
+ }
+
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ using (var engine = new Engine())
+ using (var store = engine.CreateStore())
+ using (var module = store.CreateModule("hello.wasm"))
+ using (dynamic instance = module.Instantiate(new Host()))
+ {
+ instance.run();
+ }
+ }
+ }
+}
diff --git a/crates/misc/dotnet/examples/hello/hello.csproj b/crates/misc/dotnet/examples/hello/hello.csproj
new file mode 100644
index 0000000000..0ac0a26c74
--- /dev/null
+++ b/crates/misc/dotnet/examples/hello/hello.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ netcoreapp3.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/crates/misc/dotnet/examples/hello/hello.wasm b/crates/misc/dotnet/examples/hello/hello.wasm
new file mode 100644
index 0000000000..dbc6d58cb1
Binary files /dev/null and b/crates/misc/dotnet/examples/hello/hello.wasm differ
diff --git a/crates/misc/dotnet/examples/hello/hello.wat b/crates/misc/dotnet/examples/hello/hello.wat
new file mode 100644
index 0000000000..f7fb8df1aa
--- /dev/null
+++ b/crates/misc/dotnet/examples/hello/hello.wat
@@ -0,0 +1,8 @@
+(module
+ (type $t0 (func))
+ (import "" "hello" (func $.hello (type $t0)))
+ (func $run
+ call $.hello
+ )
+ (export "run" (func $run))
+)
diff --git a/crates/misc/dotnet/examples/memory/Program.cs b/crates/misc/dotnet/examples/memory/Program.cs
new file mode 100644
index 0000000000..f42b806fec
--- /dev/null
+++ b/crates/misc/dotnet/examples/memory/Program.cs
@@ -0,0 +1,31 @@
+using System;
+using Wasmtime;
+
+namespace HelloExample
+{
+ class Host : IHost
+ {
+ public Instance Instance { get; set; }
+
+ [Import("log")]
+ public void Log(int address, int length)
+ {
+ var message = Instance.Externs.Memories[0].ReadString(address, length);
+ Console.WriteLine($"Message from WebAssembly: {message}");
+ }
+ }
+
+ class Program
+ {
+ static void Main(string[] args)
+ {
+ using (var engine = new Engine())
+ using (var store = engine.CreateStore())
+ using (var module = store.CreateModule("memory.wasm"))
+ using (dynamic instance = module.Instantiate(new Host()))
+ {
+ instance.run();
+ }
+ }
+ }
+}
diff --git a/crates/misc/dotnet/examples/memory/memory.csproj b/crates/misc/dotnet/examples/memory/memory.csproj
new file mode 100644
index 0000000000..0ac0a26c74
--- /dev/null
+++ b/crates/misc/dotnet/examples/memory/memory.csproj
@@ -0,0 +1,21 @@
+
+
+
+ Exe
+ netcoreapp3.0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/crates/misc/dotnet/examples/memory/memory.wasm b/crates/misc/dotnet/examples/memory/memory.wasm
new file mode 100644
index 0000000000..700ec7a856
Binary files /dev/null and b/crates/misc/dotnet/examples/memory/memory.wasm differ
diff --git a/crates/misc/dotnet/examples/memory/memory.wat b/crates/misc/dotnet/examples/memory/memory.wat
new file mode 100644
index 0000000000..c052570786
--- /dev/null
+++ b/crates/misc/dotnet/examples/memory/memory.wat
@@ -0,0 +1,12 @@
+(module
+ (type $t0 (func (param i32 i32)))
+ (import "" "log" (func $.log (type $t0)))
+ (memory (export "mem") 1 2)
+ (data (i32.const 0) "Hello World")
+ (func $run
+ i32.const 0
+ i32.const 11
+ call $.log
+ )
+ (export "run" (func $run))
+)
diff --git a/crates/misc/dotnet/src/Bindings/Binding.cs b/crates/misc/dotnet/src/Bindings/Binding.cs
new file mode 100644
index 0000000000..f8d1a972a3
--- /dev/null
+++ b/crates/misc/dotnet/src/Bindings/Binding.cs
@@ -0,0 +1,129 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using Wasmtime.Imports;
+
+namespace Wasmtime.Bindings
+{
+ ///
+ /// Represents an abstract host binding.
+ ///
+ public abstract class Binding
+ {
+ internal abstract SafeHandle Bind(Store store, IHost host);
+
+ internal static void ThrowBindingException(Import import, MemberInfo member, string message)
+ {
+ throw new WasmtimeException($"Unable to bind '{member.DeclaringType.Name}.{member.Name}' to WebAssembly import '{import}': {message}.");
+ }
+
+ internal static List GetImportBindings(IHost host, Module module)
+ {
+ if (host is null)
+ {
+ throw new ArgumentNullException(nameof(host));
+ }
+
+ if (module is null)
+ {
+ throw new ArgumentNullException(nameof(module));
+ }
+
+ var flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
+ var type = host.GetType();
+ var methods = type.GetMethods(flags).Where(m => !m.IsSpecialName && Attribute.IsDefined(m, typeof(ImportAttribute)));
+ var fields = type.GetFields(flags).Where(m => !m.IsSpecialName && Attribute.IsDefined(m, typeof(ImportAttribute)));
+
+ var bindings = new List();
+ foreach (var import in module.Imports.All)
+ {
+ switch (import)
+ {
+ case FunctionImport func:
+ bindings.Add(BindFunction(func, methods));
+ break;
+
+ case GlobalImport global:
+ bindings.Add(BindGlobal(global, fields));
+ break;
+
+ case MemoryImport memory:
+ bindings.Add(BindMemory(memory, fields));
+ break;
+
+ default:
+ throw new NotSupportedException("Unsupported import binding type.");
+ }
+ }
+
+ return bindings;
+ }
+
+ private static FunctionBinding BindFunction(FunctionImport import, IEnumerable methods)
+ {
+ var method = methods.Where(m =>
+ {
+ var attribute = (ImportAttribute)m.GetCustomAttribute(typeof(ImportAttribute));
+ if (attribute is null)
+ {
+ return false;
+ }
+
+ return attribute.Name == import.Name &&
+ ((string.IsNullOrEmpty(attribute.Module) &&
+ string.IsNullOrEmpty(import.ModuleName)) ||
+ attribute.Module == import.ModuleName);
+ }
+ ).FirstOrDefault();
+
+ if (method is null)
+ {
+ throw new WasmtimeException($"Failed to bind function import '{import}': the host does not contain a method with a matching 'Import' attribute.");
+ }
+
+ return new FunctionBinding(import, method);
+ }
+
+ private static GlobalBinding BindGlobal(GlobalImport import, IEnumerable fields)
+ {
+ var field = fields.Where(f =>
+ {
+ var attribute = (ImportAttribute)f.GetCustomAttribute(typeof(ImportAttribute));
+ return attribute.Name == import.Name &&
+ ((string.IsNullOrEmpty(attribute.Module) &&
+ string.IsNullOrEmpty(import.ModuleName)) ||
+ attribute.Module == import.ModuleName);
+ }
+ ).FirstOrDefault();
+
+ if (field is null)
+ {
+ throw new WasmtimeException($"Failed to bind global import '{import}': the host does not contain a global field with a matching 'Import' attribute.");
+ }
+
+ return new GlobalBinding(import, field);
+ }
+
+ private static MemoryBinding BindMemory(MemoryImport import, IEnumerable fields)
+ {
+ var field = fields.Where(f =>
+ {
+ var attribute = (ImportAttribute)f.GetCustomAttribute(typeof(ImportAttribute));
+ return attribute.Name == import.Name &&
+ ((string.IsNullOrEmpty(attribute.Module) &&
+ string.IsNullOrEmpty(import.ModuleName)) ||
+ attribute.Module == import.ModuleName);
+ }
+ ).FirstOrDefault();
+
+ if (field is null)
+ {
+ throw new WasmtimeException($"Failed to bind memory import '{import}': the host does not contain a memory field with a matching 'Import' attribute.");
+ }
+
+ return new MemoryBinding(import, field);
+ }
+ }
+}
diff --git a/crates/misc/dotnet/src/Bindings/FunctionBinding.cs b/crates/misc/dotnet/src/Bindings/FunctionBinding.cs
new file mode 100644
index 0000000000..1941b20cf8
--- /dev/null
+++ b/crates/misc/dotnet/src/Bindings/FunctionBinding.cs
@@ -0,0 +1,346 @@
+using System;
+using System.Linq;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+using Wasmtime.Imports;
+
+namespace Wasmtime.Bindings
+{
+ ///
+ /// Represents a host function binding.
+ ///
+ public class FunctionBinding : Binding
+ {
+ ///
+ /// Constructs a new function binding.
+ ///
+ /// The function import of the binding.
+ /// The method the import is bound to.
+ public FunctionBinding(FunctionImport import, MethodInfo method)
+ {
+ if (import is null)
+ {
+ throw new ArgumentNullException(nameof(import));
+ }
+
+ if (method is null)
+ {
+ throw new ArgumentNullException(nameof(method));
+ }
+
+ Import = import;
+ Method = method;
+
+ Validate();
+ }
+
+ ///
+ /// The function import of the binding.
+ ///
+ public FunctionImport Import { get; private set; }
+
+ ///
+ /// The method the import is bound to.
+ ///
+ public MethodInfo Method { get; private set; }
+
+ internal override SafeHandle Bind(Store store, IHost host)
+ {
+ unsafe
+ {
+ if (_callback != null)
+ {
+ throw new InvalidOperationException("Cannot bind more than once.");
+ }
+
+ _callback = CreateCallback(store, host);
+
+ var parameters = Interop.ToValueTypeVec(Import.Parameters);
+ var results = Interop.ToValueTypeVec(Import.Results);
+ using (var funcType = Interop.wasm_functype_new(ref parameters, ref results))
+ {
+ return Interop.wasm_func_new(store.Handle, funcType, _callback);
+ }
+ }
+ }
+
+ private void Validate()
+ {
+ if (Method.IsStatic)
+ {
+ ThrowBindingException(Import, Method, "method cannot be static");
+ }
+
+ if (Method.IsGenericMethod)
+ {
+ ThrowBindingException(Import, Method, "method cannot be generic");
+ }
+
+ if (Method.IsConstructor)
+ {
+ ThrowBindingException(Import, Method, "method cannot be a constructor");
+ }
+
+ ValidateParameters();
+
+ ValidateReturnType();
+ }
+
+ private void ValidateParameters()
+ {
+ var parameters = Method.GetParameters();
+ if (parameters.Length != Import.Parameters.Count)
+ {
+ ThrowBindingException(
+ Import,
+ Method,
+ $"parameter mismatch: import requires {Import.Parameters.Count} but the method has {parameters.Length}");
+ }
+
+ for (int i = 0; i < parameters.Length; ++i)
+ {
+ var parameter = parameters[i];
+ if (parameter.ParameterType.IsByRef)
+ {
+ if (parameter.IsOut)
+ {
+ ThrowBindingException(Import, Method, $"parameter '{parameter.Name}' cannot be an 'out' parameter");
+ }
+ else
+ {
+ ThrowBindingException(Import, Method, $"parameter '{parameter.Name}' cannot be a 'ref' parameter");
+ }
+ }
+
+ var expected = Import.Parameters[i];
+ if (!Interop.TryGetValueKind(parameter.ParameterType, out var kind) || !Interop.IsMatchingKind(kind, expected))
+ {
+ ThrowBindingException(Import, Method, $"method parameter '{parameter.Name}' is expected to be of type '{Interop.ToString(expected)}'");
+ }
+ }
+ }
+
+ private void ValidateReturnType()
+ {
+ int resultsCount = Import.Results.Count();
+ if (resultsCount == 0)
+ {
+ if (Method.ReturnType != typeof(void))
+ {
+ ThrowBindingException(Import, Method, "method must return void");
+ }
+ }
+ else if (resultsCount == 1)
+ {
+ var expected = Import.Results[0];
+ if (!Interop.TryGetValueKind(Method.ReturnType, out var kind) || !Interop.IsMatchingKind(kind, expected))
+ {
+ ThrowBindingException(Import, Method, $"return type is expected to be '{Interop.ToString(expected)}'");
+ }
+ }
+ else
+ {
+ if (!IsTupleOfSize(Method.ReturnType, resultsCount))
+ {
+ ThrowBindingException(Import, Method, $"return type is expected to be a tuple of size {resultsCount}");
+ }
+
+ var typeArguments =
+ Method.ReturnType.GetGenericArguments().SelectMany(type =>
+ {
+ if (type.IsConstructedGenericType)
+ {
+ return type.GenericTypeArguments;
+ }
+ return Enumerable.Repeat(type, 1);
+ });
+
+ int i = 0;
+ foreach (var typeArgument in typeArguments)
+ {
+ var expected = Import.Results[i];
+ if (!Interop.TryGetValueKind(typeArgument, out var kind) || !Interop.IsMatchingKind(kind, expected))
+ {
+ ThrowBindingException(Import, Method, $"return tuple item #{i} is expected to be of type '{Interop.ToString(expected)}'");
+ }
+
+ ++i;
+ }
+ }
+ }
+
+ private static bool IsTupleOfSize(Type type, int size)
+ {
+ if (!type.IsConstructedGenericType)
+ {
+ return false;
+ }
+
+ var definition = type.GetGenericTypeDefinition();
+
+ if (size == 0)
+ {
+ return definition == typeof(ValueTuple);
+ }
+
+ if (size == 1)
+ {
+ return definition == typeof(ValueTuple<>);
+ }
+
+ if (size == 2)
+ {
+ return definition == typeof(ValueTuple<,>);
+ }
+
+ if (size == 3)
+ {
+ return definition == typeof(ValueTuple<,,>);
+ }
+
+ if (size == 4)
+ {
+ return definition == typeof(ValueTuple<,,,>);
+ }
+
+ if (size == 5)
+ {
+ return definition == typeof(ValueTuple<,,,,>);
+ }
+
+ if (size == 6)
+ {
+ return definition == typeof(ValueTuple<,,,,,>);
+ }
+
+ if (size == 7)
+ {
+ return definition == typeof(ValueTuple<,,,,,,>);
+ }
+
+ if (definition != typeof(ValueTuple<,,,,,,,>))
+ {
+ return false;
+ }
+
+ return IsTupleOfSize(type.GetGenericArguments().Last(), size - 7);
+ }
+
+ private unsafe Interop.WasmFuncCallback CreateCallback(Store store, IHost host)
+ {
+ var args = new object[Import.Parameters.Count];
+ bool hasReturn = Method.ReturnType != typeof(void);
+ var storeHandle = store.Handle;
+
+ Interop.WasmFuncCallback callback = (arguments, results) =>
+ {
+ try
+ {
+ SetArgs(arguments, args);
+
+ var result = Method.Invoke(host, args);
+
+ if (hasReturn)
+ {
+ SetResults(result, results);
+ }
+ return IntPtr.Zero;
+ }
+ catch (TargetInvocationException ex)
+ {
+ var bytes = Encoding.UTF8.GetBytes(ex.InnerException.Message + "\0" /* exception messages need a null */);
+
+ fixed (byte* ptr = bytes)
+ {
+ Interop.wasm_byte_vec_t message = new Interop.wasm_byte_vec_t();
+ message.size = (UIntPtr)bytes.Length;
+ message.data = ptr;
+
+ return Interop.wasm_trap_new(storeHandle, ref message);
+ }
+ }
+ };
+
+ return callback;
+ }
+
+ private static unsafe void SetArgs(Interop.wasm_val_t* arguments, object[] args)
+ {
+ for (int i = 0; i < args.Length; ++i)
+ {
+ var arg = arguments[i];
+
+ switch (arg.kind)
+ {
+ case Interop.wasm_valkind_t.WASM_I32:
+ args[i] = arg.of.i32;
+ break;
+
+ case Interop.wasm_valkind_t.WASM_I64:
+ args[i] = arg.of.i64;
+ break;
+
+ case Interop.wasm_valkind_t.WASM_F32:
+ args[i] = arg.of.f32;
+ break;
+
+ case Interop.wasm_valkind_t.WASM_F64:
+ args[i] = arg.of.f64;
+ break;
+
+ default:
+ throw new NotSupportedException("Unsupported value type.");
+ }
+ }
+ }
+
+ private static unsafe void SetResults(object value, Interop.wasm_val_t* results)
+ {
+ var tuple = value as ITuple;
+ if (tuple is null)
+ {
+ SetResult(value, &results[0]);
+ }
+ else
+ {
+ for (int i = 0; i < tuple.Length; ++i)
+ {
+ SetResults(tuple[i], &results[i]);
+ }
+ }
+ }
+
+ private static unsafe void SetResult(object value, Interop.wasm_val_t* result)
+ {
+ switch (value)
+ {
+ case int i:
+ result->kind = Interop.wasm_valkind_t.WASM_I32;
+ result->of.i32 = i;
+ break;
+
+ case long l:
+ result->kind = Interop.wasm_valkind_t.WASM_I64;
+ result->of.i64 = l;
+ break;
+
+ case float f:
+ result->kind = Interop.wasm_valkind_t.WASM_F32;
+ result->of.f32 = f;
+ break;
+
+ case double d:
+ result->kind = Interop.wasm_valkind_t.WASM_F64;
+ result->of.f64 = d;
+ break;
+
+ default:
+ throw new NotSupportedException("Unsupported return value type.");
+ }
+ }
+
+ private Interop.WasmFuncCallback _callback;
+ }
+}
diff --git a/crates/misc/dotnet/src/Bindings/GlobalBinding.cs b/crates/misc/dotnet/src/Bindings/GlobalBinding.cs
new file mode 100644
index 0000000000..33ebeb71c0
--- /dev/null
+++ b/crates/misc/dotnet/src/Bindings/GlobalBinding.cs
@@ -0,0 +1,125 @@
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using Wasmtime.Imports;
+
+namespace Wasmtime.Bindings
+{
+ ///
+ /// Represents a host global binding.
+ ///
+ public class GlobalBinding : Binding
+ {
+ ///
+ /// Constructs a new global binding.
+ ///
+ /// The global import of the binding.
+ /// The field the import is bound to.
+ public GlobalBinding(GlobalImport import, FieldInfo field)
+ {
+ if (import is null)
+ {
+ throw new ArgumentNullException(nameof(import));
+ }
+
+ if (field is null)
+ {
+ throw new ArgumentNullException(nameof(field));
+ }
+
+ Import = import;
+ Field = field;
+
+ Validate();
+ }
+
+ ///
+ /// The global import of the binding.
+ ///
+ public GlobalImport Import { get; private set; }
+
+ ///
+ /// The field the import is bound to.
+ ///
+ public FieldInfo Field { get; private set; }
+
+ internal override SafeHandle Bind(Store store, IHost host)
+ {
+ unsafe
+ {
+ dynamic global = Field.GetValue(host);
+ if (global.Handle != null)
+ {
+ throw new InvalidOperationException("Cannot bind more than once.");
+ }
+
+ var v = Interop.ToValue((object)global.InitialValue, Import.Kind);
+
+ var valueType = Interop.wasm_valtype_new(v.kind);
+ var valueTypeHandle = valueType.DangerousGetHandle();
+ valueType.SetHandleAsInvalid();
+
+ using (var globalType = Interop.wasm_globaltype_new(
+ valueTypeHandle,
+ Import.IsMutable ? Interop.wasm_mutability_t.WASM_VAR : Interop.wasm_mutability_t.WASM_CONST))
+ {
+ var handle = Interop.wasm_global_new(store.Handle, globalType, &v);
+ global.Handle = handle;
+ return handle;
+ }
+ }
+ }
+
+ private void Validate()
+ {
+ if (Field.IsStatic)
+ {
+ ThrowBindingException(Import, Field, "field cannot be static");
+ }
+
+ if (!Field.IsInitOnly)
+ {
+ ThrowBindingException(Import, Field, "field must be readonly");
+ }
+
+ if (!Field.FieldType.IsGenericType)
+ {
+ ThrowBindingException(Import, Field, "field is expected to be of type 'Global'");
+ }
+
+ var definition = Field.FieldType.GetGenericTypeDefinition();
+ if (definition == typeof(Global<>))
+ {
+ if (Import.IsMutable)
+ {
+ ThrowBindingException(Import, Field, "the import is mutable (use the 'MutableGlobal' type)");
+ }
+ }
+ else if (definition == typeof(MutableGlobal<>))
+ {
+ if (!Import.IsMutable)
+ {
+ ThrowBindingException(Import, Field, "the import is constant (use the 'Global' type)");
+ }
+ }
+ else
+ {
+ ThrowBindingException(Import, Field, "field is expected to be of type 'Global' or 'MutableGlobal'");
+ }
+
+ var arg = Field.FieldType.GetGenericArguments()[0];
+
+ if (Interop.TryGetValueKind(arg, out var kind))
+ {
+ if (!Interop.IsMatchingKind(kind, Import.Kind))
+ {
+ ThrowBindingException(Import, Field, $"global type argument is expected to be of type '{Interop.ToString(Import.Kind)}'");
+ }
+ }
+ else
+ {
+ ThrowBindingException(Import, Field, $"'{arg}' is not a valid global type");
+ }
+ }
+ }
+}
diff --git a/crates/misc/dotnet/src/Bindings/MemoryBinding.cs b/crates/misc/dotnet/src/Bindings/MemoryBinding.cs
new file mode 100644
index 0000000000..ea6c860f96
--- /dev/null
+++ b/crates/misc/dotnet/src/Bindings/MemoryBinding.cs
@@ -0,0 +1,98 @@
+using System;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using Wasmtime.Imports;
+
+namespace Wasmtime.Bindings
+{
+ ///
+ /// Represents a host memory binding.
+ ///
+ public class MemoryBinding : Binding
+ {
+ ///
+ /// Constructs a new memory binding.
+ ///
+ /// The memory import of the binding.
+ /// The field the import is bound to.
+ public MemoryBinding(MemoryImport import, FieldInfo field)
+ {
+ if (import is null)
+ {
+ throw new ArgumentNullException(nameof(import));
+ }
+
+ if (field is null)
+ {
+ throw new ArgumentNullException(nameof(field));
+ }
+
+ Import = import;
+ Field = field;
+
+ Validate();
+ }
+
+ ///
+ /// The memory import of the binding.
+ ///
+ public MemoryImport Import { get; private set; }
+
+ ///
+ /// The field the import is bound to.
+ ///
+ public FieldInfo Field { get; private set; }
+
+ internal override SafeHandle Bind(Store store, IHost host)
+ {
+ dynamic memory = Field.GetValue(host);
+ if (memory.Handle != null)
+ {
+ throw new InvalidOperationException("Cannot bind more than once.");
+ }
+
+ uint min = memory.Minimum;
+ uint max = memory.Maximum;
+
+ if (min != Import.Minimum)
+ {
+ ThrowBindingException(Import, Field, $"Memory does not have the expected minimum of {Import.Minimum} page(s)");
+ }
+ if (max != Import.Maximum)
+ {
+ ThrowBindingException(Import, Field, $"Memory does not have the expected maximum of {Import.Maximum} page(s)");
+ }
+
+ unsafe
+ {
+ Interop.wasm_limits_t limits = new Interop.wasm_limits_t();
+ limits.min = min;
+ limits.max = max;
+ using (var memoryType = Interop.wasm_memorytype_new(&limits))
+ {
+ var handle = Interop.wasm_memory_new(store.Handle, memoryType);
+ memory.Handle = handle;
+ return handle;
+ }
+ }
+ }
+
+ private void Validate()
+ {
+ if (Field.IsStatic)
+ {
+ ThrowBindingException(Import, Field, "field cannot be static");
+ }
+
+ if (!Field.IsInitOnly)
+ {
+ ThrowBindingException(Import, Field, "field must be readonly");
+ }
+
+ if (Field.FieldType != typeof(Memory))
+ {
+ ThrowBindingException(Import, Field, "field is expected to be of type 'Memory'");
+ }
+ }
+ }
+}
diff --git a/crates/misc/dotnet/src/Engine.cs b/crates/misc/dotnet/src/Engine.cs
new file mode 100644
index 0000000000..20b727a4ed
--- /dev/null
+++ b/crates/misc/dotnet/src/Engine.cs
@@ -0,0 +1,44 @@
+using System;
+
+namespace Wasmtime
+{
+ ///
+ /// Represents the Wasmtime engine.
+ ///
+ public class Engine : IDisposable
+ {
+ ///
+ /// Constructs a new .
+ ///
+ public Engine()
+ {
+ Handle = Interop.wasm_engine_new();
+
+ if (Handle.IsInvalid)
+ {
+ throw new WasmtimeException("Failed to create Wasmtime engine.");
+ }
+ }
+
+ ///
+ /// Creates a new Wasmtime .
+ ///
+ /// Returns the new .
+ public Store CreateStore()
+ {
+ return new Store(this);
+ }
+
+ ///
+ public void Dispose()
+ {
+ if (!Handle.IsInvalid)
+ {
+ Handle.Dispose();
+ Handle.SetHandleAsInvalid();
+ }
+ }
+
+ internal Interop.EngineHandle Handle { get; private set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/Exports/Export.cs b/crates/misc/dotnet/src/Exports/Export.cs
new file mode 100644
index 0000000000..047ad7025e
--- /dev/null
+++ b/crates/misc/dotnet/src/Exports/Export.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Wasmtime.Exports
+{
+ ///
+ /// Represents an export of a WebAssembly module.
+ ///
+ public abstract class Export
+ {
+ internal Export(IntPtr exportType)
+ {
+ unsafe
+ {
+ var name = Interop.wasm_exporttype_name(exportType);
+ Name = Marshal.PtrToStringUTF8((IntPtr)name->data, (int)name->size);
+ }
+ }
+
+ ///
+ /// The name of the export.
+ ///
+ public string Name { get; private set; }
+
+ ///
+ public override string ToString()
+ {
+ return Name;
+ }
+ }
+}
diff --git a/crates/misc/dotnet/src/Exports/Exports.cs b/crates/misc/dotnet/src/Exports/Exports.cs
new file mode 100644
index 0000000000..52d50c3c48
--- /dev/null
+++ b/crates/misc/dotnet/src/Exports/Exports.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Generic;
+
+namespace Wasmtime.Exports
+{
+ ///
+ /// Represents the exports of a WebAssembly module.
+ ///
+ public class Exports
+ {
+ internal Exports(Module module)
+ {
+ Interop.wasm_exporttype_vec_t exports;
+ Interop.wasm_module_exports(module.Handle, out exports);
+
+ try
+ {
+ var all = new List((int)exports.size);
+ var functions = new List();
+ var globals = new List();
+ var tables = new List();
+ var memories = new List();
+
+ for (int i = 0; i < (int)exports.size; ++i)
+ {
+ unsafe
+ {
+ var exportType = exports.data[i];
+ var externType = Interop.wasm_exporttype_type(exportType);
+
+ switch (Interop.wasm_externtype_kind(externType))
+ {
+ case Interop.wasm_externkind_t.WASM_EXTERN_FUNC:
+ var function = new FunctionExport(exportType, externType);
+ functions.Add(function);
+ all.Add(function);
+ break;
+
+ case Interop.wasm_externkind_t.WASM_EXTERN_GLOBAL:
+ var global = new GlobalExport(exportType, externType);
+ globals.Add(global);
+ all.Add(global);
+ break;
+
+ case Interop.wasm_externkind_t.WASM_EXTERN_TABLE:
+ var table = new TableExport(exportType, externType);
+ tables.Add(table);
+ all.Add(table);
+ break;
+
+ case Interop.wasm_externkind_t.WASM_EXTERN_MEMORY:
+ var memory = new MemoryExport(exportType, externType);
+ memories.Add(memory);
+ all.Add(memory);
+ break;
+
+ default:
+ throw new NotSupportedException("Unsupported export extern type.");
+ }
+ }
+ }
+
+ Functions = functions;
+ Globals = globals;
+ Tables = tables;
+ Memories = memories;
+ All = all;
+ }
+ finally
+ {
+ Interop.wasm_exporttype_vec_delete(ref exports);
+ }
+ }
+
+ ///
+ /// The exported functions of a WebAssembly module.
+ ///
+ public IReadOnlyList Functions { get; private set; }
+
+ ///
+ /// The exported globals of a WebAssembly module.
+ ///
+ public IReadOnlyList Globals { get; private set; }
+
+ ///
+ /// The exported tables of a WebAssembly module.
+ ///
+ public IReadOnlyList Tables { get; private set; }
+
+ ///
+ /// The exported memories of a WebAssembly module.
+ ///
+ public IReadOnlyList Memories { get; private set; }
+
+ internal List All { get; private set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/Exports/FunctionExport.cs b/crates/misc/dotnet/src/Exports/FunctionExport.cs
new file mode 100644
index 0000000000..1b57c65cb3
--- /dev/null
+++ b/crates/misc/dotnet/src/Exports/FunctionExport.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace Wasmtime.Exports
+{
+ ///
+ /// Represents a function exported from a WebAssembly module.
+ ///
+ public class FunctionExport : Export
+ {
+ internal FunctionExport(IntPtr exportType, IntPtr externType) : base(exportType)
+ {
+ Debug.Assert(Interop.wasm_externtype_kind(externType) == Interop.wasm_externkind_t.WASM_EXTERN_FUNC);
+
+ unsafe
+ {
+ var funcType = Interop.wasm_externtype_as_functype_const(externType);
+ Parameters = Interop.ToValueKindList(Interop.wasm_functype_params(funcType));
+ Results = Interop.ToValueKindList(Interop.wasm_functype_results(funcType));
+ }
+ }
+
+ ///
+ /// The parameter of the exported WebAssembly function.
+ ///
+ public IReadOnlyList Parameters { get; private set; }
+
+ ///
+ /// The results of the exported WebAssembly function.
+ ///
+ public IReadOnlyList Results { get; private set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/Exports/GlobalExport.cs b/crates/misc/dotnet/src/Exports/GlobalExport.cs
new file mode 100644
index 0000000000..3dc76be560
--- /dev/null
+++ b/crates/misc/dotnet/src/Exports/GlobalExport.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Diagnostics;
+
+namespace Wasmtime.Exports
+{
+ ///
+ /// Represents a global variable exported from a WebAssembly module.
+ ///
+ public class GlobalExport : Export
+ {
+ internal GlobalExport(IntPtr exportType, IntPtr externType) : base(exportType)
+ {
+ Debug.Assert(Interop.wasm_externtype_kind(externType) == Interop.wasm_externkind_t.WASM_EXTERN_GLOBAL);
+
+ var globalType = Interop.wasm_externtype_as_globaltype_const(externType);
+ Kind = Interop.wasm_valtype_kind(Interop.wasm_globaltype_content(globalType));
+ IsMutable = Interop.wasm_globaltype_mutability(globalType) == Interop.wasm_mutability_t.WASM_VAR;
+ }
+
+ ///
+ /// The kind of value for the global variable.
+ ///
+ public ValueKind Kind { get; private set; }
+
+ ///
+ /// Determines whether or not the global variable is mutable.
+ ///
+ public bool IsMutable { get; private set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/Exports/MemoryExport.cs b/crates/misc/dotnet/src/Exports/MemoryExport.cs
new file mode 100644
index 0000000000..f5f200e39a
--- /dev/null
+++ b/crates/misc/dotnet/src/Exports/MemoryExport.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Diagnostics;
+
+namespace Wasmtime.Exports
+{
+ ///
+ /// Represents a memory exported from a WebAssembly module.
+ ///
+ public class MemoryExport : Export
+ {
+ internal MemoryExport(IntPtr exportType, IntPtr externType) : base(exportType)
+ {
+ Debug.Assert(Interop.wasm_externtype_kind(externType) == Interop.wasm_externkind_t.WASM_EXTERN_MEMORY);
+
+ var memoryType = Interop.wasm_externtype_as_memorytype_const(externType);
+
+ unsafe
+ {
+ var limits = Interop.wasm_memorytype_limits(memoryType);
+ Minimum = limits->min;
+ Maximum = limits->max;
+ }
+ }
+
+ ///
+ /// The minimum memory size (in WebAssembly page units).
+ ///
+ public uint Minimum { get; private set; }
+
+ ///
+ /// The maximum memory size (in WebAssembly page units).
+ ///
+ public uint Maximum { get; private set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/Exports/TableExport.cs b/crates/misc/dotnet/src/Exports/TableExport.cs
new file mode 100644
index 0000000000..1643aee68d
--- /dev/null
+++ b/crates/misc/dotnet/src/Exports/TableExport.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Diagnostics;
+
+namespace Wasmtime.Exports
+{
+ ///
+ /// Represents a table exported from a WebAssembly module.
+ ///
+ public class TableExport : Export
+ {
+ internal TableExport(IntPtr exportType, IntPtr externType) : base(exportType)
+ {
+ Debug.Assert(Interop.wasm_externtype_kind(externType) == Interop.wasm_externkind_t.WASM_EXTERN_TABLE);
+
+ var tableType = Interop.wasm_externtype_as_tabletype_const(externType);
+
+ Kind = Interop.wasm_valtype_kind(Interop.wasm_tabletype_element(tableType));
+
+ unsafe
+ {
+ var limits = Interop.wasm_tabletype_limits(tableType);
+ Minimum = limits->min;
+ Maximum = limits->max;
+ }
+ }
+
+ ///
+ /// The value kind of the table.
+ ///
+ public ValueKind Kind { get; private set; }
+
+ ///
+ /// The minimum number of elements in the table.
+ ///
+ public uint Minimum { get; private set; }
+
+ ///
+ /// The maximum number of elements in the table.
+ ///
+ public uint Maximum { get; private set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/Externs/ExternFunction.cs b/crates/misc/dotnet/src/Externs/ExternFunction.cs
new file mode 100644
index 0000000000..462f3b9ffe
--- /dev/null
+++ b/crates/misc/dotnet/src/Externs/ExternFunction.cs
@@ -0,0 +1,87 @@
+using System;
+using System.Collections.Generic;
+using Wasmtime.Exports;
+
+namespace Wasmtime.Externs
+{
+ ///
+ /// Represents an external (instantiated) WebAssembly function.
+ ///
+ public class ExternFunction
+ {
+ internal ExternFunction(FunctionExport export, IntPtr func)
+ {
+ _export = export;
+ _func = func;
+ }
+
+ ///
+ /// The name of the WebAssembly function.
+ ///
+ public string Name => _export.Name;
+
+ ///
+ /// The parameters of the WebAssembly function.
+ ///
+ public IReadOnlyList Parameters => _export.Parameters;
+
+ ///
+ /// The results of the WebAssembly function.
+ ///
+ public IReadOnlyList Results => _export.Results;
+
+ ///
+ /// Invokes the WebAssembly function.
+ ///
+ /// The array of arguments to pass to the function.
+ ///
+ /// Returns null if the function has no return value.
+ /// Returns the value if the function returns a single value.
+ /// Returns an array of values if the function returns more than one value.
+ ///
+ public object Invoke(params object[] arguments)
+ {
+ if (arguments.Length != Parameters.Count)
+ {
+ throw new WasmtimeException($"Argument mismatch when invoking function '{Name}': requires {Parameters.Count} but was given {arguments.Length}.");
+ }
+
+ unsafe
+ {
+ Interop.wasm_val_t* args = stackalloc Interop.wasm_val_t[Parameters.Count];
+ Interop.wasm_val_t* results = stackalloc Interop.wasm_val_t[Results.Count];
+
+ for (int i = 0; i < arguments.Length; ++i)
+ {
+ args[i] = Interop.ToValue(arguments[i], Parameters[i]);
+ }
+
+ var trap = Interop.wasm_func_call(_func, args, results);
+ if (trap != IntPtr.Zero)
+ {
+ throw TrapException.FromOwnedTrap(trap);
+ }
+
+ if (Results.Count == 0)
+ {
+ return null;
+ }
+
+ if (Results.Count == 1)
+ {
+ return Interop.ToObject(&results[0]);
+ }
+
+ var ret = new object[Results.Count];
+ for (int i = 0; i < Results.Count; ++i)
+ {
+ ret[i] = Interop.ToObject(&results[i]);
+ }
+ return ret;
+ }
+ }
+
+ private FunctionExport _export;
+ private IntPtr _func;
+ }
+}
diff --git a/crates/misc/dotnet/src/Externs/ExternGlobal.cs b/crates/misc/dotnet/src/Externs/ExternGlobal.cs
new file mode 100644
index 0000000000..087260058c
--- /dev/null
+++ b/crates/misc/dotnet/src/Externs/ExternGlobal.cs
@@ -0,0 +1,62 @@
+using System;
+using Wasmtime.Exports;
+
+namespace Wasmtime.Externs
+{
+ ///
+ /// Represents an external (instantiated) WebAssembly global.
+ ///
+ public class ExternGlobal
+ {
+ internal ExternGlobal(GlobalExport export, IntPtr global)
+ {
+ _export = export;
+ _global = global;
+ }
+
+ ///
+ /// The name of the WebAssembly global.
+ ///
+ public string Name => _export.Name;
+
+ ///
+ /// The kind of value for the global variable.
+ ///
+ public ValueKind Kind => _export.Kind;
+
+ ///
+ /// Determines whether or not the global variable is mutable.
+ ///
+ public bool IsMutable => _export.IsMutable;
+
+ public object Value
+ {
+ get
+ {
+ unsafe
+ {
+ var v = stackalloc Interop.wasm_val_t[1];
+ Interop.wasm_global_get(_global, v);
+ return Interop.ToObject(v);
+ }
+ }
+ set
+ {
+ if (!IsMutable)
+ {
+ throw new InvalidOperationException($"The value of global '{Name}' cannot be modified.");
+ }
+
+ var v = Interop.ToValue(value, Kind);
+
+ unsafe
+ {
+ Interop.wasm_global_set(_global, &v);
+ }
+ }
+ }
+
+ private GlobalExport _export;
+ private IntPtr _global;
+ }
+}
diff --git a/crates/misc/dotnet/src/Externs/ExternMemory.cs b/crates/misc/dotnet/src/Externs/ExternMemory.cs
new file mode 100644
index 0000000000..a9011aacb7
--- /dev/null
+++ b/crates/misc/dotnet/src/Externs/ExternMemory.cs
@@ -0,0 +1,245 @@
+using System;
+using System.Buffers.Binary;
+using System.Text;
+using Wasmtime.Exports;
+
+namespace Wasmtime.Externs
+{
+ ///
+ /// Represents an external (instantiated) WebAssembly memory.
+ ///
+ public class ExternMemory
+ {
+ internal ExternMemory(MemoryExport export, IntPtr memory)
+ {
+ _export = export;
+ _memory = memory;
+ }
+
+ ///
+ /// The name of the WebAssembly memory.
+ ///
+ public string Name => _export.Name;
+
+ ///
+ /// The minimum memory size (in WebAssembly page units).
+ ///
+ public uint Minimum => _export.Minimum;
+
+ ///
+ /// The maximum memory size (in WebAssembly page units).
+ ///
+ public uint Maximum => _export.Maximum;
+
+ ///
+ /// The span of the memory.
+ ///
+ ///
+ /// The span may become invalid if the memory grows.
+ ///
+ /// This may happen if the memory is explicitly requested to grow or
+ /// grows as a result of WebAssembly execution.
+ ///
+ /// Therefore, the returned Span should not be stored.
+ ///
+ public unsafe Span Span
+ {
+ get
+ {
+ var data = Interop.wasm_memory_data(_memory);
+ var size = Convert.ToInt32(Interop.wasm_memory_data_size(_memory).ToUInt32());
+ return new Span(data, size);
+ }
+ }
+
+ ///
+ /// Reads a UTF-8 string from memory.
+ ///
+ /// The zero-based address to read from.
+ /// The length of bytes to read.
+ /// Returns the string read from memory.
+ public string ReadString(int address, int length)
+ {
+ return Encoding.UTF8.GetString(Span.Slice(address, length));
+ }
+
+ ///
+ /// Writes a UTF-8 string at the given address.
+ ///
+ /// The zero-based address to write to.
+ /// The string to write.
+ /// Returns the number of bytes written.
+ public int WriteString(int address, string value)
+ {
+ return Encoding.UTF8.GetBytes(value, Span.Slice(address));
+ }
+
+ ///
+ /// Reads a byte from memory.
+ ///
+ /// The zero-based address to read from.
+ /// Returns the byte read from memory.
+ public byte ReadByte(int address)
+ {
+ return Span[address];
+ }
+
+ ///
+ /// Writes a byte to memory.
+ ///
+ /// The zero-based address to write to.
+ /// The byte to write.
+ public void WriteByte(int address, byte value)
+ {
+ Span[address] = value;
+ }
+
+ ///
+ /// Reads a short from memory.
+ ///
+ /// The zero-based address to read from.
+ /// Returns the short read from memory.
+ public short ReadInt16(int address)
+ {
+ return BinaryPrimitives.ReadInt16LittleEndian(Span.Slice(address, 2));
+ }
+
+ ///
+ /// Writes a short to memory.
+ ///
+ /// The zero-based address to write to.
+ /// The short to write.
+ public void WriteInt16(int address, short value)
+ {
+ BinaryPrimitives.WriteInt16LittleEndian(Span.Slice(address, 2), value);
+ }
+
+ ///
+ /// Reads an int from memory.
+ ///
+ /// The zero-based address to read from.
+ /// Returns the int read from memory.
+ public int ReadInt32(int address)
+ {
+ return BinaryPrimitives.ReadInt32LittleEndian(Span.Slice(address, 4));
+ }
+
+ ///
+ /// Writes an int to memory.
+ ///
+ /// The zero-based address to write to.
+ /// The int to write.
+ public void WriteInt32(int address, int value)
+ {
+ BinaryPrimitives.WriteInt32LittleEndian(Span.Slice(address, 4), value);
+ }
+
+ ///
+ /// Reads a long from memory.
+ ///
+ /// The zero-based address to read from.
+ /// Returns the long read from memory.
+ public long ReadInt64(int address)
+ {
+ return BinaryPrimitives.ReadInt64LittleEndian(Span.Slice(address, 8));
+ }
+
+ ///
+ /// Writes a long to memory.
+ ///
+ /// The zero-based address to write to.
+ /// The long to write.
+ public void WriteInt64(int address, long value)
+ {
+ BinaryPrimitives.WriteInt64LittleEndian(Span.Slice(address, 8), value);
+ }
+
+ ///
+ /// Reads an IntPtr from memory.
+ ///
+ /// The zero-based address to read from.
+ /// Returns the IntPtr read from memory.
+ public IntPtr ReadIntPtr(int address)
+ {
+ if (IntPtr.Size == 4)
+ {
+ return (IntPtr)ReadInt32(address);
+ }
+ return (IntPtr)ReadInt64(address);
+ }
+
+ ///
+ /// Writes an IntPtr to memory.
+ ///
+ /// The zero-based address to write to.
+ /// The IntPtr to write.
+ public void WriteIntPtr(int address, IntPtr value)
+ {
+ if (IntPtr.Size == 4)
+ {
+ WriteInt32(address, value.ToInt32());
+ }
+ else
+ {
+ WriteInt64(address, value.ToInt64());
+ }
+ }
+
+ ///
+ /// Reads a long from memory.
+ ///
+ /// The zero-based address to read from.
+ /// Returns the long read from memory.
+ public float ReadSingle(int address)
+ {
+ unsafe
+ {
+ var i = ReadInt32(address);
+ return *((float*)&i);
+ }
+ }
+
+ ///
+ /// Writes a single to memory.
+ ///
+ /// The zero-based address to write to.
+ /// The single to write.
+ public void WriteSingle(int address, float value)
+ {
+ unsafe
+ {
+ WriteInt32(address, *(int*)&value);
+ }
+ }
+
+ ///
+ /// Reads a double from memory.
+ ///
+ /// The zero-based address to read from.
+ /// Returns the double read from memory.
+ public double ReadDouble(int address)
+ {
+ unsafe
+ {
+ var i = ReadInt64(address);
+ return *((double*)&i);
+ }
+ }
+
+ ///
+ /// Writes a double to memory.
+ ///
+ /// The zero-based address to write to.
+ /// The double to write.
+ public void WriteDouble(int address, double value)
+ {
+ unsafe
+ {
+ WriteInt64(address, *(long*)&value);
+ }
+ }
+
+ private MemoryExport _export;
+ private IntPtr _memory;
+ }
+}
diff --git a/crates/misc/dotnet/src/Externs/Externs.cs b/crates/misc/dotnet/src/Externs/Externs.cs
new file mode 100644
index 0000000000..078f179180
--- /dev/null
+++ b/crates/misc/dotnet/src/Externs/Externs.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using Wasmtime.Exports;
+
+namespace Wasmtime.Externs
+{
+ ///
+ /// Represents external (instantiated) WebAssembly functions, globals, tables, and memories.
+ ///
+ public class Externs
+ {
+ internal Externs(Wasmtime.Exports.Exports exports, Interop.wasm_extern_vec_t externs)
+ {
+ var functions = new List();
+ var globals = new List();
+ var memories = new List();
+
+ for (int i = 0; i < (int)externs.size; ++i)
+ {
+ unsafe
+ {
+ var ext = externs.data[i];
+
+ switch (Interop.wasm_extern_kind(ext))
+ {
+ case Interop.wasm_externkind_t.WASM_EXTERN_FUNC:
+ var function = new ExternFunction((FunctionExport)exports.All[i], Interop.wasm_extern_as_func(ext));
+ functions.Add(function);
+ break;
+
+ case Interop.wasm_externkind_t.WASM_EXTERN_GLOBAL:
+ var global = new ExternGlobal((GlobalExport)exports.All[i], Interop.wasm_extern_as_global(ext));
+ globals.Add(global);
+ break;
+
+ case Interop.wasm_externkind_t.WASM_EXTERN_MEMORY:
+ var memory = new ExternMemory((MemoryExport)exports.All[i], Interop.wasm_extern_as_memory(ext));
+ memories.Add(memory);
+ break;
+
+ default:
+ throw new NotSupportedException("Unsupported extern type.");
+ }
+ }
+ }
+
+ Functions = functions;
+ Globals = globals;
+ Memories = memories;
+ }
+
+ ///
+ /// The extern functions from an instantiated WebAssembly module.
+ ///
+ public IReadOnlyList Functions { get; private set; }
+
+ ///
+ /// The extern globals from an instantiated WebAssembly module.
+ ///
+ public IReadOnlyList Globals { get; private set; }
+
+ ///
+ /// The extern memories from an instantiated WebAssembly module.
+ ///
+ public IReadOnlyList Memories { get; private set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/Global.cs b/crates/misc/dotnet/src/Global.cs
new file mode 100644
index 0000000000..13ca0e285e
--- /dev/null
+++ b/crates/misc/dotnet/src/Global.cs
@@ -0,0 +1,50 @@
+using System;
+
+namespace Wasmtime
+{
+ ///
+ /// Represents a constant WebAssembly global value.
+ ///
+ public class Global
+ {
+ ///
+ /// Creates a new with the given initial value.
+ ///
+ /// The initial value of the global.
+ public Global(T initialValue)
+ {
+ InitialValue = initialValue;
+ Kind = Interop.ToValueKind(typeof(T));
+ }
+
+ ///
+ /// The value of the global.
+ ///
+ public T Value
+ {
+ get
+ {
+ if (Handle is null)
+ {
+ throw new InvalidOperationException("The global cannot be used before it is bound to a module instance.");
+ }
+
+ unsafe
+ {
+ var v = stackalloc Interop.wasm_val_t[1];
+
+ Interop.wasm_global_get(Handle.DangerousGetHandle(), v);
+
+ // TODO: figure out a way that doesn't box the value
+ return (T)Interop.ToObject(v);
+ }
+ }
+ }
+
+ internal ValueKind Kind { get; private set; }
+
+ internal Interop.GlobalHandle Handle { get; set; }
+
+ internal T InitialValue { get; private set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/IHost.cs b/crates/misc/dotnet/src/IHost.cs
new file mode 100644
index 0000000000..f8a3f4175f
--- /dev/null
+++ b/crates/misc/dotnet/src/IHost.cs
@@ -0,0 +1,25 @@
+using System;
+using System.Collections.Generic;
+using Wasmtime.Bindings;
+
+namespace Wasmtime
+{
+ ///
+ /// The interface implemented by Wasmtime hosts.
+ ///
+ public interface IHost
+ {
+ ///
+ /// The that the host is bound to.
+ ///
+ /// A host can only bind to one module instance at a time.
+ Instance Instance { get; set; }
+
+ ///
+ /// Gets the import bindings of the host given a WebAssembly module.
+ ///
+ /// The WebAssembly module to get the import bindings for.
+ /// Returns the list of import bindings for the host.
+ List GetImportBindings(Module module) => Binding.GetImportBindings(this, module);
+ }
+}
diff --git a/crates/misc/dotnet/src/ImportAttribute.cs b/crates/misc/dotnet/src/ImportAttribute.cs
new file mode 100644
index 0000000000..ff06fc4cb3
--- /dev/null
+++ b/crates/misc/dotnet/src/ImportAttribute.cs
@@ -0,0 +1,31 @@
+using System;
+
+namespace Wasmtime
+{
+ ///
+ /// Used to mark .NET methods and fields as imports to a WebAssembly module.
+ ///
+ [AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
+ public class ImportAttribute : Attribute
+ {
+ ///
+ /// Constructs a new .
+ ///
+ /// The name of the import.
+ public ImportAttribute(string name)
+ {
+ Name = name;
+ }
+
+ ///
+ /// The name of the import.
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// The module name of the import.
+ ///
+ /// A null or empty module name implies that the import is not scoped to a module.
+ public string Module { get; set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/Imports/FunctionImport.cs b/crates/misc/dotnet/src/Imports/FunctionImport.cs
new file mode 100644
index 0000000000..0d963a657e
--- /dev/null
+++ b/crates/misc/dotnet/src/Imports/FunctionImport.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace Wasmtime.Imports
+{
+ ///
+ /// Represents a function imported to a WebAssembly module.
+ ///
+ public class FunctionImport : Import
+ {
+ internal FunctionImport(IntPtr importType, IntPtr externType) : base(importType)
+ {
+ Debug.Assert(Interop.wasm_externtype_kind(externType) == Interop.wasm_externkind_t.WASM_EXTERN_FUNC);
+
+ unsafe
+ {
+ var funcType = Interop.wasm_externtype_as_functype_const(externType);
+ Parameters = Interop.ToValueKindList(Interop.wasm_functype_params(funcType));
+ Results = Interop.ToValueKindList(Interop.wasm_functype_results(funcType));
+ }
+ }
+
+ ///
+ /// The parameters of the imported function.
+ ///
+ public IReadOnlyList Parameters { get; private set; }
+
+ ///
+ /// The results of the imported function.
+ ///
+ public IReadOnlyList Results { get; private set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/Imports/GlobalImport.cs b/crates/misc/dotnet/src/Imports/GlobalImport.cs
new file mode 100644
index 0000000000..0f92b40f19
--- /dev/null
+++ b/crates/misc/dotnet/src/Imports/GlobalImport.cs
@@ -0,0 +1,30 @@
+using System;
+using System.Diagnostics;
+
+namespace Wasmtime.Imports
+{
+ ///
+ /// Represents a global variable imported to a WebAssembly module.
+ ///
+ public class GlobalImport : Import
+ {
+ internal GlobalImport(IntPtr importType, IntPtr externType) : base(importType)
+ {
+ Debug.Assert(Interop.wasm_externtype_kind(externType) == Interop.wasm_externkind_t.WASM_EXTERN_GLOBAL);
+
+ var globalType = Interop.wasm_externtype_as_globaltype_const(externType);
+ Kind = Interop.wasm_valtype_kind(Interop.wasm_globaltype_content(globalType));
+ IsMutable = Interop.wasm_globaltype_mutability(globalType) == Interop.wasm_mutability_t.WASM_VAR;
+ }
+
+ ///
+ /// The kind of value for the global variable.
+ ///
+ public ValueKind Kind { get; private set; }
+
+ ///
+ /// Determines whether or not the global variable is mutable.
+ ///
+ public bool IsMutable { get; private set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/Imports/Import.cs b/crates/misc/dotnet/src/Imports/Import.cs
new file mode 100644
index 0000000000..f2e8115027
--- /dev/null
+++ b/crates/misc/dotnet/src/Imports/Import.cs
@@ -0,0 +1,39 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Wasmtime.Imports
+{
+ ///
+ /// The base class for import types.
+ ///
+ public abstract class Import
+ {
+ internal Import(IntPtr importType)
+ {
+ unsafe
+ {
+ var moduleName = Interop.wasm_importtype_module(importType);
+ ModuleName = Marshal.PtrToStringUTF8((IntPtr)moduleName->data, (int)moduleName->size);
+
+ var name = Interop.wasm_importtype_name(importType);
+ Name = Marshal.PtrToStringUTF8((IntPtr)name->data, (int)name->size);
+ }
+ }
+
+ ///
+ /// The module name of the import.
+ ///
+ public string ModuleName { get; private set; }
+
+ ///
+ /// The name of the import.
+ ///
+ public string Name { get; private set; }
+
+ ///
+ public override string ToString()
+ {
+ return $"{ModuleName}{(string.IsNullOrEmpty(ModuleName) ? "" : ".")}{Name}";
+ }
+ }
+}
diff --git a/crates/misc/dotnet/src/Imports/Imports.cs b/crates/misc/dotnet/src/Imports/Imports.cs
new file mode 100644
index 0000000000..153fee3363
--- /dev/null
+++ b/crates/misc/dotnet/src/Imports/Imports.cs
@@ -0,0 +1,97 @@
+using System;
+using System.Collections.Generic;
+
+namespace Wasmtime.Imports
+{
+ ///
+ /// Represents imported functions, globals, tables, and memories to a WebAssembly module.
+ ///
+ public class Imports
+ {
+ internal Imports(Module module)
+ {
+ Interop.wasm_importtype_vec_t imports;
+ Interop.wasm_module_imports(module.Handle, out imports);
+
+ try
+ {
+ var all = new List((int)imports.size);
+ var functions = new List();
+ var globals = new List();
+ var tables = new List();
+ var memories = new List();
+
+ for (int i = 0; i < (int)imports.size; ++i)
+ {
+ unsafe
+ {
+ var importType = imports.data[i];
+ var externType = Interop.wasm_importtype_type(importType);
+
+ switch (Interop.wasm_externtype_kind(externType))
+ {
+ case Interop.wasm_externkind_t.WASM_EXTERN_FUNC:
+ var function = new FunctionImport(importType, externType);
+ functions.Add(function);
+ all.Add(function);
+ break;
+
+ case Interop.wasm_externkind_t.WASM_EXTERN_GLOBAL:
+ var global = new GlobalImport(importType, externType);
+ globals.Add(global);
+ all.Add(global);
+ break;
+
+ case Interop.wasm_externkind_t.WASM_EXTERN_TABLE:
+ var table = new TableImport(importType, externType);
+ tables.Add(table);
+ all.Add(table);
+ break;
+
+ case Interop.wasm_externkind_t.WASM_EXTERN_MEMORY:
+ var memory = new MemoryImport(importType, externType);
+ memories.Add(memory);
+ all.Add(memory);
+ break;
+
+ default:
+ throw new NotSupportedException("Unsupported import extern type.");
+ }
+ }
+ }
+
+ Functions = functions;
+ Globals = globals;
+ Tables = tables;
+ Memories = memories;
+ All = all;
+ }
+ finally
+ {
+ Interop.wasm_importtype_vec_delete(ref imports);
+ }
+ }
+
+ ///
+ /// The imported functions required by a WebAssembly module.
+ ///
+ public IReadOnlyList Functions { get; private set; }
+
+ ///
+ /// The imported globals required by a WebAssembly module.
+ ///
+ public IReadOnlyList Globals { get; private set; }
+
+ ///
+ /// The imported tables required by a WebAssembly module.
+ ///
+ public IReadOnlyList Tables { get; private set; }
+
+ ///
+ /// The imported memories required by a WebAssembly module.
+ ///
+ public IReadOnlyList Memories { get; private set; }
+
+ internal IReadOnlyList All { get; private set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/Imports/MemoryImport.cs b/crates/misc/dotnet/src/Imports/MemoryImport.cs
new file mode 100644
index 0000000000..3b6769c888
--- /dev/null
+++ b/crates/misc/dotnet/src/Imports/MemoryImport.cs
@@ -0,0 +1,35 @@
+using System;
+using System.Diagnostics;
+
+namespace Wasmtime.Imports
+{
+ ///
+ /// Represents a memory imported to a WebAssembly module.
+ ///
+ public class MemoryImport : Import
+ {
+ internal MemoryImport(IntPtr importType, IntPtr externType) : base(importType)
+ {
+ Debug.Assert(Interop.wasm_externtype_kind(externType) == Interop.wasm_externkind_t.WASM_EXTERN_MEMORY);
+
+ var memoryType = Interop.wasm_externtype_as_memorytype_const(externType);
+
+ unsafe
+ {
+ var limits = Interop.wasm_memorytype_limits(memoryType);
+ Minimum = limits->min;
+ Maximum = limits->max;
+ }
+ }
+
+ ///
+ /// The minimum memory size (in WebAssembly page units).
+ ///
+ public uint Minimum { get; private set; }
+
+ ///
+ /// The maximum memory size (in WebAssembly page units).
+ ///
+ public uint Maximum { get; private set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/Imports/TableImport.cs b/crates/misc/dotnet/src/Imports/TableImport.cs
new file mode 100644
index 0000000000..352a4c1047
--- /dev/null
+++ b/crates/misc/dotnet/src/Imports/TableImport.cs
@@ -0,0 +1,42 @@
+using System;
+using System.Diagnostics;
+
+namespace Wasmtime.Imports
+{
+ ///
+ /// Represents a table imported to a WebAssembly module.
+ ///
+ public class TableImport : Import
+ {
+ internal TableImport(IntPtr importType, IntPtr externType) : base(importType)
+ {
+ Debug.Assert(Interop.wasm_externtype_kind(externType) == Interop.wasm_externkind_t.WASM_EXTERN_TABLE);
+
+ var tableType = Interop.wasm_externtype_as_tabletype_const(externType);
+
+ Kind = Interop.wasm_valtype_kind(Interop.wasm_tabletype_element(tableType));
+
+ unsafe
+ {
+ var limits = Interop.wasm_tabletype_limits(tableType);
+ Minimum = limits->min;
+ Maximum = limits->max;
+ }
+ }
+
+ ///
+ /// The value kind of the table.
+ ///
+ public ValueKind Kind { get; private set; }
+
+ ///
+ /// The minimum number of elements in the table.
+ ///
+ public uint Minimum { get; private set; }
+
+ ///
+ /// The maximum number of elements in the table.
+ ///
+ public uint Maximum { get; private set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/Instance.cs b/crates/misc/dotnet/src/Instance.cs
new file mode 100644
index 0000000000..c145d2be83
--- /dev/null
+++ b/crates/misc/dotnet/src/Instance.cs
@@ -0,0 +1,145 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Dynamic;
+using Wasmtime.Externs;
+
+namespace Wasmtime
+{
+ ///
+ /// Represents an instantiated WebAssembly module.
+ ///
+ public class Instance : DynamicObject, IDisposable
+ {
+ internal Instance(Module module, IHost host)
+ {
+ Host = host;
+ Module = module;
+
+ var bindings = host.GetImportBindings(module);
+ var handles = bindings.Select(b => b.Bind(module.Store, host)).ToList();
+
+ unsafe
+ {
+ Handle = Interop.wasm_instance_new(
+ Module.Store.Handle,
+ Module.Handle,
+ handles.Select(h => ToExtern(h)).ToArray(),
+ out var trap);
+
+ if (trap != IntPtr.Zero)
+ {
+ throw TrapException.FromOwnedTrap(trap);
+ }
+ }
+
+ if (Handle.IsInvalid)
+ {
+ throw new WasmtimeException($"Failed to instantiate module '{module.Name}'.");
+ }
+
+ // Dispose of all function handles (not needed at runtime)
+ foreach (var h in handles.Where(h => h is Interop.FunctionHandle))
+ {
+ h.Dispose();
+ }
+
+ Interop.wasm_instance_exports(Handle, out _externs);
+
+ Externs = new Wasmtime.Externs.Externs(Module.Exports, _externs);
+
+ _functions = Externs.Functions.ToDictionary(f => f.Name);
+ _globals = Externs.Globals.ToDictionary(g => g.Name);
+ }
+
+ ///
+ /// The host associated with this instance.
+ ///
+ public IHost Host { get; private set; }
+
+ ///
+ /// The WebAssembly module associated with the instantiation.
+ ///
+ public Module Module { get; private set; }
+
+ ///
+ /// The external (instantiated) collection of functions, globals, tables, and memories.
+ ///
+ public Wasmtime.Externs.Externs Externs { get; private set; }
+
+ ///
+ public void Dispose()
+ {
+ if (!Handle.IsInvalid)
+ {
+ Handle.Dispose();
+ Handle.SetHandleAsInvalid();
+ }
+ if (_externs.size != UIntPtr.Zero)
+ {
+ Interop.wasm_extern_vec_delete(ref _externs);
+ _externs.size = UIntPtr.Zero;
+ }
+ }
+
+ ///
+ public override bool TryGetMember(GetMemberBinder binder, out object result)
+ {
+ if (_globals.TryGetValue(binder.Name, out var global))
+ {
+ result = global.Value;
+ return true;
+ }
+ result = null;
+ return false;
+ }
+
+ ///
+ public override bool TrySetMember(SetMemberBinder binder, object value)
+ {
+ if (_globals.TryGetValue(binder.Name, out var global))
+ {
+ global.Value = value;
+ return true;
+ }
+ return false;
+ }
+
+ ///
+ public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
+ {
+ if (!_functions.TryGetValue(binder.Name, out var func))
+ {
+ result = null;
+ return false;
+ }
+
+ result = func.Invoke(args);
+ return true;
+ }
+
+ private static unsafe IntPtr ToExtern(SafeHandle handle)
+ {
+ switch (handle)
+ {
+ case Interop.FunctionHandle f:
+ return Interop.wasm_func_as_extern(f);
+
+ case Interop.GlobalHandle g:
+ return Interop.wasm_global_as_extern(g);
+
+ case Interop.MemoryHandle m:
+ return Interop.wasm_memory_as_extern(m);
+
+ default:
+ throw new NotSupportedException("Unexpected handle type.");
+ }
+ }
+
+ internal Interop.InstanceHandle Handle { get; private set; }
+ private Interop.wasm_extern_vec_t _externs;
+ private Dictionary _functions;
+ private Dictionary _globals;
+ }
+}
diff --git a/crates/misc/dotnet/src/Interop.cs b/crates/misc/dotnet/src/Interop.cs
new file mode 100644
index 0000000000..a7d9796175
--- /dev/null
+++ b/crates/misc/dotnet/src/Interop.cs
@@ -0,0 +1,762 @@
+using System;
+using System.Collections.Generic;
+using System.Runtime.InteropServices;
+
+namespace Wasmtime
+{
+ ///
+ /// Implements the Wasmtime API bindings.
+ ///
+ /// See https://github.com/WebAssembly/wasm-c-api/blob/master/include/wasm.h for the C API reference.
+ internal static class Interop
+ {
+ internal class EngineHandle : SafeHandle
+ {
+ public EngineHandle() : base(IntPtr.Zero, true)
+ {
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.wasm_engine_delete(handle);
+ return true;
+ }
+ }
+
+ internal class StoreHandle : SafeHandle
+ {
+ public StoreHandle() : base(IntPtr.Zero, true)
+ {
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.wasm_store_delete(handle);
+ return true;
+ }
+ }
+
+ internal class ModuleHandle : SafeHandle
+ {
+ public ModuleHandle() : base(IntPtr.Zero, true)
+ {
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.wasm_module_delete(handle);
+ return true;
+ }
+ }
+
+ internal class FunctionHandle : SafeHandle
+ {
+ public FunctionHandle() : base(IntPtr.Zero, true)
+ {
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.wasm_func_delete(handle);
+ return true;
+ }
+ }
+
+ internal class GlobalHandle : SafeHandle
+ {
+ public GlobalHandle() : base(IntPtr.Zero, true)
+ {
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.wasm_global_delete(handle);
+ return true;
+ }
+ }
+
+ internal class MemoryHandle : SafeHandle
+ {
+ public MemoryHandle() : base(IntPtr.Zero, true)
+ {
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.wasm_memory_delete(handle);
+ return true;
+ }
+ }
+
+ internal class InstanceHandle : SafeHandle
+ {
+ public InstanceHandle() : base(IntPtr.Zero, true)
+ {
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.wasm_instance_delete(handle);
+ return true;
+ }
+ }
+
+ internal class FuncTypeHandle : SafeHandle
+ {
+ public FuncTypeHandle() : base(IntPtr.Zero, true)
+ {
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.wasm_functype_delete(handle);
+ return true;
+ }
+ }
+
+ internal class GlobalTypeHandle : SafeHandle
+ {
+ public GlobalTypeHandle() : base(IntPtr.Zero, true)
+ {
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.wasm_globaltype_delete(handle);
+ return true;
+ }
+ }
+
+ internal class MemoryTypeHandle : SafeHandle
+ {
+ public MemoryTypeHandle() : base(IntPtr.Zero, true)
+ {
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.wasm_memorytype_delete(handle);
+ return true;
+ }
+ }
+
+ internal class ValueTypeHandle : SafeHandle
+ {
+ public ValueTypeHandle() : base(IntPtr.Zero, true)
+ {
+ }
+
+ public override bool IsInvalid => handle == IntPtr.Zero;
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.wasm_valtype_delete(handle);
+ return true;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct wasm_byte_vec_t
+ {
+ public UIntPtr size;
+ public byte* data;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct wasm_valtype_vec_t
+ {
+ public UIntPtr size;
+ public IntPtr* data;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct wasm_export_vec_t
+ {
+ public UIntPtr size;
+ public IntPtr* data;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct wasm_extern_vec_t
+ {
+ public UIntPtr size;
+ public IntPtr* data;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct wasm_importtype_vec_t
+ {
+ public UIntPtr size;
+ public IntPtr* data;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct wasm_exporttype_vec_t
+ {
+ public UIntPtr size;
+ public IntPtr* data;
+ }
+
+ internal enum wasm_valkind_t : byte
+ {
+ WASM_I32,
+ WASM_I64,
+ WASM_F32,
+ WASM_F64,
+ WASM_ANYREF = 128,
+ WASM_FUNCREF,
+ }
+
+ [StructLayout(LayoutKind.Explicit)]
+ internal struct wasm_val_union_t
+ {
+ [FieldOffset(0)]
+ public int i32;
+
+ [FieldOffset(0)]
+ public long i64;
+
+ [FieldOffset(0)]
+ public float f32;
+
+ [FieldOffset(0)]
+ public double f64;
+
+ [FieldOffset(0)]
+ public IntPtr reference;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct wasm_val_t
+ {
+ public wasm_valkind_t kind;
+ public wasm_val_union_t of;
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct wasm_limits_t
+ {
+ public uint min;
+
+ public uint max;
+ }
+
+ public static wasm_val_t ToValue(object o, ValueKind kind)
+ {
+ wasm_val_t value = new wasm_val_t();
+ switch (kind)
+ {
+ case ValueKind.Int32:
+ value.kind = wasm_valkind_t.WASM_I32;
+ value.of.i32 = (int)Convert.ChangeType(o, TypeCode.Int32);
+ break;
+
+ case ValueKind.Int64:
+ value.kind = wasm_valkind_t.WASM_I64;
+ value.of.i64 = (long)Convert.ChangeType(o, TypeCode.Int64);
+ break;
+
+ case ValueKind.Float32:
+ value.kind = wasm_valkind_t.WASM_F32;
+ value.of.f32 = (float)Convert.ChangeType(o, TypeCode.Single);
+ break;
+
+ case ValueKind.Float64:
+ value.kind = wasm_valkind_t.WASM_F64;
+ value.of.f64 = (double)Convert.ChangeType(o, TypeCode.Double);
+ break;
+
+ // TODO: support AnyRef
+
+ default:
+ throw new NotSupportedException("Unsupported value type.");
+ }
+ return value;
+ }
+
+ public static unsafe object ToObject(wasm_val_t* v)
+ {
+ switch (v->kind)
+ {
+ case Interop.wasm_valkind_t.WASM_I32:
+ return v->of.i32;
+
+ case Interop.wasm_valkind_t.WASM_I64:
+ return v->of.i64;
+
+ case Interop.wasm_valkind_t.WASM_F32:
+ return v->of.f32;
+
+ case Interop.wasm_valkind_t.WASM_F64:
+ return v->of.f64;
+
+ // TODO: support AnyRef
+
+ default:
+ throw new NotSupportedException("Unsupported value kind.");
+ }
+ }
+
+ public static bool TryGetValueKind(Type type, out ValueKind kind)
+ {
+ if (type == typeof(int))
+ {
+ kind = ValueKind.Int32;
+ return true;
+ }
+
+ if (type == typeof(long))
+ {
+ kind = ValueKind.Int64;
+ return true;
+ }
+
+ if (type == typeof(float))
+ {
+ kind = ValueKind.Float32;
+ return true;
+ }
+
+ if (type == typeof(double))
+ {
+ kind = ValueKind.Float64;
+ return true;
+ }
+
+ kind = default(ValueKind);
+ return false;
+ }
+
+ public static ValueKind ToValueKind(Type type)
+ {
+ if (TryGetValueKind(type, out var kind))
+ {
+ return kind;
+ }
+
+ throw new NotSupportedException($"Type '{type}' is not a supported WebAssembly value type.");
+ }
+
+ public static string ToString(ValueKind kind)
+ {
+ switch (kind)
+ {
+ case ValueKind.Int32:
+ return "int";
+
+ case ValueKind.Int64:
+ return "long";
+
+ case ValueKind.Float32:
+ return "float";
+
+ case ValueKind.Float64:
+ return "double";
+
+ default:
+ throw new NotSupportedException("Unsupported value kind.");
+ }
+ }
+
+ public static bool IsMatchingKind(ValueKind kind, ValueKind expected)
+ {
+ if (kind == expected)
+ {
+ return true;
+ }
+
+ if (expected == ValueKind.AnyRef)
+ {
+ return kind == ValueKind.FuncRef;
+ }
+
+ return false;
+ }
+
+ internal unsafe delegate IntPtr WasmFuncCallback(wasm_val_t* parameters, wasm_val_t* results);
+
+ internal enum wasm_externkind_t : byte
+ {
+ WASM_EXTERN_FUNC,
+ WASM_EXTERN_GLOBAL,
+ WASM_EXTERN_TABLE,
+ WASM_EXTERN_MEMORY,
+ }
+
+ internal enum wasm_mutability_t : byte
+ {
+ WASM_CONST,
+ WASM_VAR,
+ }
+
+ internal static unsafe List ToValueKindList(Interop.wasm_valtype_vec_t* vec)
+ {
+ var list = new List((int)vec->size);
+
+ for (int i = 0; i < (int)vec->size; ++i)
+ {
+ list.Add(Interop.wasm_valtype_kind(vec->data[i]));
+ }
+
+ return list;
+ }
+
+ internal static Interop.wasm_valtype_vec_t ToValueTypeVec(IReadOnlyList collection)
+ {
+ Interop.wasm_valtype_vec_t vec;
+ Interop.wasm_valtype_vec_new_uninitialized(out vec, (UIntPtr)collection.Count);
+
+ int i = 0;
+ foreach (var type in collection)
+ {
+ var valType = Interop.wasm_valtype_new((wasm_valkind_t)type);
+ unsafe
+ {
+ vec.data[i++] = valType.DangerousGetHandle();
+ }
+ valType.SetHandleAsInvalid();
+ }
+
+ return vec;
+ }
+
+ // Engine imports
+
+ [DllImport("wasmtime")]
+ public static extern EngineHandle wasm_engine_new();
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_engine_delete(IntPtr engine);
+
+ // Store imports
+
+ [DllImport("wasmtime")]
+ public static extern StoreHandle wasm_store_new(EngineHandle engine);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_store_delete(IntPtr engine);
+
+ // Byte vec imports
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_byte_vec_new_empty(out wasm_byte_vec_t vec);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_byte_vec_new_uninitialized(out wasm_byte_vec_t vec, UIntPtr length);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_byte_vec_new(out wasm_byte_vec_t vec, UIntPtr length, byte[] data);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_byte_vec_copy(out wasm_byte_vec_t vec, ref wasm_byte_vec_t src);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_byte_vec_delete(ref wasm_byte_vec_t vec);
+
+ // Value type vec imports
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_valtype_vec_new_empty(out wasm_valtype_vec_t vec);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_valtype_vec_new_uninitialized(out wasm_valtype_vec_t vec, UIntPtr length);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_valtype_vec_new(out wasm_valtype_vec_t vec, UIntPtr length, IntPtr[] data);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_valtype_vec_copy(out wasm_valtype_vec_t vec, ref wasm_valtype_vec_t src);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_valtype_vec_delete(ref wasm_valtype_vec_t vec);
+
+ // Extern vec imports
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_extern_vec_new_empty(out wasm_extern_vec_t vec);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_extern_vec_new_uninitialized(out wasm_extern_vec_t vec, UIntPtr length);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_extern_vec_new(out wasm_extern_vec_t vec, UIntPtr length, IntPtr[] data);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_extern_vec_copy(out wasm_extern_vec_t vec, ref wasm_extern_vec_t src);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_extern_vec_delete(ref wasm_extern_vec_t vec);
+
+ // Import type vec imports
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_importtype_vec_new_empty(out wasm_importtype_vec_t vec);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_importtype_vec_new_uninitialized(out wasm_importtype_vec_t vec, UIntPtr length);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_importtype_vec_new(out wasm_importtype_vec_t vec, UIntPtr length, IntPtr[] data);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_importtype_vec_copy(out wasm_importtype_vec_t vec, ref wasm_importtype_vec_t src);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_importtype_vec_delete(ref wasm_importtype_vec_t vec);
+
+ // Export type vec imports
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_exporttype_vec_new_empty(out wasm_exporttype_vec_t vec);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_exporttype_vec_new_uninitialized(out wasm_exporttype_vec_t vec, UIntPtr length);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_exporttype_vec_new(out wasm_exporttype_vec_t vec, UIntPtr length, IntPtr[] data);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_exporttype_vec_copy(out wasm_exporttype_vec_t vec, ref wasm_exporttype_vec_t src);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_exporttype_vec_delete(ref wasm_exporttype_vec_t vec);
+
+ // Import type imports
+
+ [DllImport("wasmtime")]
+ public static extern unsafe wasm_byte_vec_t* wasm_importtype_module(IntPtr importType);
+
+ [DllImport("wasmtime")]
+ public static extern unsafe wasm_byte_vec_t* wasm_importtype_name(IntPtr importType);
+
+ [DllImport("wasmtime")]
+ public static extern unsafe IntPtr wasm_importtype_type(IntPtr importType);
+
+ // Export type imports
+
+ [DllImport("wasmtime")]
+ public static extern unsafe wasm_byte_vec_t* wasm_exporttype_name(IntPtr exportType);
+
+ [DllImport("wasmtime")]
+ public static extern unsafe IntPtr wasm_exporttype_type(IntPtr exportType);
+
+ // Module imports
+
+ [DllImport("wasmtime")]
+ public static extern ModuleHandle wasm_module_new(StoreHandle store, ref wasm_byte_vec_t bytes);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_module_imports(ModuleHandle module, out wasm_importtype_vec_t imports);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_module_exports(ModuleHandle module, out wasm_exporttype_vec_t exports);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_module_delete(IntPtr module);
+
+ // Value type imports
+
+ [DllImport("wasmtime")]
+ public static extern ValueTypeHandle wasm_valtype_new(wasm_valkind_t kind);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_valtype_delete(IntPtr valueType);
+
+ [DllImport("wasmtime")]
+ public static extern ValueKind wasm_valtype_kind(IntPtr valueType);
+
+ // Extern imports
+
+ [DllImport("wasmtime")]
+ public static extern wasm_externkind_t wasm_extern_kind(IntPtr ext);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_extern_type(IntPtr ext);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_extern_as_func(IntPtr ext);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_extern_as_global(IntPtr ext);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_extern_as_table(IntPtr ext);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_extern_as_memory(IntPtr ext);
+
+ // Extern type imports
+
+ [DllImport("wasmtime")]
+ public static extern wasm_externkind_t wasm_externtype_kind(IntPtr externType);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_externtype_as_functype_const(IntPtr externType);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_externtype_as_globaltype_const(IntPtr externType);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_externtype_as_tabletype_const(IntPtr externType);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_externtype_as_memorytype_const(IntPtr externType);
+
+ // Function imports
+
+ [DllImport("wasmtime")]
+ public static extern FunctionHandle wasm_func_new(StoreHandle store, FuncTypeHandle type, WasmFuncCallback callback);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_func_delete(IntPtr function);
+
+ [DllImport("wasmtime")]
+ public static unsafe extern IntPtr wasm_func_call(IntPtr function, wasm_val_t* args, wasm_val_t* results);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_func_as_extern(FunctionHandle function);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_global_as_extern(GlobalHandle global);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_memory_as_extern(MemoryHandle memory);
+
+ // Function type imports
+
+ [DllImport("wasmtime")]
+ public static extern unsafe wasm_valtype_vec_t* wasm_functype_params(IntPtr funcType);
+
+ [DllImport("wasmtime")]
+ public static extern unsafe wasm_valtype_vec_t* wasm_functype_results(IntPtr funcType);
+
+ // Instance imports
+
+ [DllImport("wasmtime")]
+ public static extern unsafe InstanceHandle wasm_instance_new(StoreHandle store, ModuleHandle module, IntPtr[] imports, out IntPtr trap);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_instance_delete(IntPtr ext);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_instance_exports(InstanceHandle instance, out wasm_extern_vec_t exports);
+
+ // Function type imports
+
+ [DllImport("wasmtime")]
+ public static extern FuncTypeHandle wasm_functype_new(ref wasm_valtype_vec_t parameters, ref wasm_valtype_vec_t results);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_functype_delete(IntPtr functype);
+
+ // Global type imports
+
+ [DllImport("wasmtime")]
+ public static extern GlobalTypeHandle wasm_globaltype_new(IntPtr valueType, wasm_mutability_t mutability);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_globaltype_delete(IntPtr globalType);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_globaltype_content(IntPtr globalType);
+
+ [DllImport("wasmtime")]
+ public static extern wasm_mutability_t wasm_globaltype_mutability(IntPtr globalType);
+
+ // Memory type imports
+
+ [DllImport("wasmtime")]
+ public static extern unsafe MemoryTypeHandle wasm_memorytype_new(wasm_limits_t* limits);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_memorytype_delete(IntPtr memoryType);
+
+
+ [DllImport("wasmtime")]
+ public static extern unsafe wasm_limits_t* wasm_memorytype_limits(MemoryTypeHandle memoryType);
+
+ // Trap imports
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_trap_new(StoreHandle store, ref wasm_byte_vec_t message);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_trap_delete(IntPtr trap);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_trap_message(IntPtr trap, out wasm_byte_vec_t message);
+
+ // Table type imports
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_tabletype_element(IntPtr tableType);
+
+ [DllImport("wasmtime")]
+ public static unsafe extern wasm_limits_t* wasm_tabletype_limits(IntPtr tableType);
+
+ // Memory type imports
+
+ [DllImport("wasmtime")]
+ public static unsafe extern wasm_limits_t* wasm_memorytype_limits(IntPtr memoryType);
+
+ // Global imports
+
+ [DllImport("wasmtime")]
+ public static unsafe extern GlobalHandle wasm_global_new(StoreHandle handle, GlobalTypeHandle globalType, wasm_val_t* initialValue);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_global_delete(IntPtr global);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_global_type(IntPtr global);
+
+ [DllImport("wasmtime")]
+ public static unsafe extern void wasm_global_get(IntPtr global, wasm_val_t* value);
+
+ [DllImport("wasmtime")]
+ public static unsafe extern void wasm_global_set(IntPtr global, wasm_val_t* value);
+
+ // Memory imports
+
+ [DllImport("wasmtime")]
+ public static extern MemoryHandle wasm_memory_new(StoreHandle handle, MemoryTypeHandle memoryType);
+
+ [DllImport("wasmtime")]
+ public static extern void wasm_memory_delete(IntPtr memory);
+
+ [DllImport("wasmtime")]
+ public static extern IntPtr wasm_memory_type(MemoryHandle memory);
+
+ [DllImport("wasmtime")]
+ public static unsafe extern byte* wasm_memory_data(IntPtr memory);
+
+ [DllImport("wasmtime")]
+ public static extern UIntPtr wasm_memory_data_size(IntPtr memory);
+
+ [DllImport("wasmtime")]
+ public static extern uint wasm_memory_size(MemoryHandle memory);
+
+ [DllImport("wasmtime")]
+ public static extern bool wasm_memory_grow(MemoryHandle memory, uint delta);
+ }
+}
diff --git a/crates/misc/dotnet/src/Memory.cs b/crates/misc/dotnet/src/Memory.cs
new file mode 100644
index 0000000000..2cfe060f81
--- /dev/null
+++ b/crates/misc/dotnet/src/Memory.cs
@@ -0,0 +1,270 @@
+using System;
+using System.Text;
+using System.Buffers.Binary;
+
+namespace Wasmtime
+{
+ ///
+ /// Represents a WebAssembly memory.
+ ///
+ public class Memory
+ {
+ ///
+ /// The size, in bytes, of a WebAssembly memory page.
+ ///
+ public const int PageSize = 65536;
+
+ ///
+ /// Creates a new memory with the given minimum and maximum page counts.
+ ///
+ ///
+ ///
+ public Memory(uint minimum = 1, uint maximum = uint.MaxValue)
+ {
+ if (minimum == 0)
+ {
+ throw new ArgumentException("The minimum cannot be zero..", nameof(minimum));
+ }
+
+ if (maximum < minimum)
+ {
+ throw new ArgumentException("The maximum cannot be less than the minimum.", nameof(maximum));
+ }
+
+ Minimum = minimum;
+ Maximum = maximum;
+ }
+
+ ///
+ /// The minimum memory size (in WebAssembly page units).
+ ///
+ public uint Minimum { get; private set; }
+
+ ///
+ /// The minimum memory size (in WebAssembly page units).
+ ///
+ public uint Maximum { get; private set; }
+
+ ///
+ /// The span of the memory.
+ ///
+ ///
+ /// The span may become invalid if the memory grows.
+ ///
+ /// This may happen if the memory is explicitly requested to grow or
+ /// grows as a result of WebAssembly execution.
+ ///
+ /// Therefore, the returned Span should not be stored.
+ ///
+ public unsafe Span Span
+ {
+ get
+ {
+ var data = Interop.wasm_memory_data(_handle.DangerousGetHandle());
+ var size = Convert.ToInt32(Interop.wasm_memory_data_size(_handle.DangerousGetHandle()).ToUInt32());
+ return new Span(data, size);
+ }
+ }
+
+ ///
+ /// Reads a UTF-8 string from memory.
+ ///
+ /// The zero-based address to read from.
+ /// The length of bytes to read.
+ /// Returns the string read from memory.
+ public string ReadString(int address, int length)
+ {
+ return Encoding.UTF8.GetString(Span.Slice(address, length));
+ }
+
+ ///
+ /// Writes a UTF-8 string at the given address.
+ ///
+ /// The zero-based address to write to.
+ /// The string to write.
+ /// Returns the number of bytes written.
+ public int WriteString(int address, string value)
+ {
+ return Encoding.UTF8.GetBytes(value, Span.Slice(address));
+ }
+
+ ///
+ /// Reads a byte from memory.
+ ///
+ /// The zero-based address to read from.
+ /// Returns the byte read from memory.
+ public byte ReadByte(int address)
+ {
+ return Span[address];
+ }
+
+ ///
+ /// Writes a byte to memory.
+ ///
+ /// The zero-based address to write to.
+ /// The byte to write.
+ public void WriteByte(int address, byte value)
+ {
+ Span[address] = value;
+ }
+
+ ///
+ /// Reads a short from memory.
+ ///
+ /// The zero-based address to read from.
+ /// Returns the short read from memory.
+ public short ReadInt16(int address)
+ {
+ return BinaryPrimitives.ReadInt16LittleEndian(Span.Slice(address, 2));
+ }
+
+ ///
+ /// Writes a short to memory.
+ ///
+ /// The zero-based address to write to.
+ /// The short to write.
+ public void WriteInt16(int address, short value)
+ {
+ BinaryPrimitives.WriteInt16LittleEndian(Span.Slice(address, 2), value);
+ }
+
+ ///
+ /// Reads an int from memory.
+ ///
+ /// The zero-based address to read from.
+ /// Returns the int read from memory.
+ public int ReadInt32(int address)
+ {
+ return BinaryPrimitives.ReadInt32LittleEndian(Span.Slice(address, 4));
+ }
+
+ ///
+ /// Writes an int to memory.
+ ///
+ /// The zero-based address to write to.
+ /// The int to write.
+ public void WriteInt32(int address, int value)
+ {
+ BinaryPrimitives.WriteInt32LittleEndian(Span.Slice(address, 4), value);
+ }
+
+ ///
+ /// Reads a long from memory.
+ ///
+ /// The zero-based address to read from.
+ /// Returns the long read from memory.
+ public long ReadInt64(int address)
+ {
+ return BinaryPrimitives.ReadInt64LittleEndian(Span.Slice(address, 8));
+ }
+
+ ///
+ /// Writes a long to memory.
+ ///
+ /// The zero-based address to write to.
+ /// The long to write.
+ public void WriteInt64(int address, long value)
+ {
+ BinaryPrimitives.WriteInt64LittleEndian(Span.Slice(address, 8), value);
+ }
+
+ ///
+ /// Reads an IntPtr from memory.
+ ///
+ /// The zero-based address to read from.
+ /// Returns the IntPtr read from memory.
+ public IntPtr ReadIntPtr(int address)
+ {
+ if (IntPtr.Size == 4)
+ {
+ return (IntPtr)ReadInt32(address);
+ }
+ return (IntPtr)ReadInt64(address);
+ }
+
+ ///
+ /// Writes an IntPtr to memory.
+ ///
+ /// The zero-based address to write to.
+ /// The IntPtr to write.
+ public void WriteIntPtr(int address, IntPtr value)
+ {
+ if (IntPtr.Size == 4)
+ {
+ WriteInt32(address, value.ToInt32());
+ }
+ else
+ {
+ WriteInt64(address, value.ToInt64());
+ }
+ }
+
+ ///
+ /// Reads a long from memory.
+ ///
+ /// The zero-based address to read from.
+ /// Returns the long read from memory.
+ public float ReadSingle(int address)
+ {
+ unsafe
+ {
+ var i = ReadInt32(address);
+ return *((float*)&i);
+ }
+ }
+
+ ///
+ /// Writes a single to memory.
+ ///
+ /// The zero-based address to write to.
+ /// The single to write.
+ public void WriteSingle(int address, float value)
+ {
+ unsafe
+ {
+ WriteInt32(address, *(int*)&value);
+ }
+ }
+
+ ///
+ /// Reads a double from memory.
+ ///
+ /// The zero-based address to read from.
+ /// Returns the double read from memory.
+ public double ReadDouble(int address)
+ {
+ unsafe
+ {
+ var i = ReadInt64(address);
+ return *((double*)&i);
+ }
+ }
+
+ ///
+ /// Writes a double to memory.
+ ///
+ /// The zero-based address to write to.
+ /// The double to write.
+ public void WriteDouble(int address, double value)
+ {
+ unsafe
+ {
+ WriteInt64(address, *(long*)&value);
+ }
+ }
+
+ internal Interop.MemoryHandle Handle
+ {
+ get
+ {
+ return _handle;
+ }
+ set
+ {
+ _handle = value;
+ }
+ }
+
+ private Interop.MemoryHandle _handle;
+ }
+}
diff --git a/crates/misc/dotnet/src/Module.cs b/crates/misc/dotnet/src/Module.cs
new file mode 100644
index 0000000000..c1f0dda6c3
--- /dev/null
+++ b/crates/misc/dotnet/src/Module.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Runtime.InteropServices;
+
+namespace Wasmtime
+{
+ ///
+ /// Represents a WebAssembly module.
+ ///
+ public class Module : IDisposable
+ {
+ internal Module(Store store, string name, byte[] bytes)
+ {
+ if (store.Handle.IsInvalid)
+ {
+ throw new ArgumentNullException(nameof(store));
+ }
+
+ var bytesHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
+
+ try
+ {
+ unsafe
+ {
+ Interop.wasm_byte_vec_t vec;
+ vec.size = (UIntPtr)bytes.Length;
+ vec.data = (byte*)bytesHandle.AddrOfPinnedObject();
+
+ Handle = Interop.wasm_module_new(store.Handle, ref vec);
+ }
+
+ if (Handle.IsInvalid)
+ {
+ throw new WasmtimeException($"WebAssembly module '{name}' is not valid.");
+ }
+ }
+ finally
+ {
+ bytesHandle.Free();
+ }
+
+ Store = store;
+ Name = name;
+ Imports = new Wasmtime.Imports.Imports(this);
+ Exports = new Wasmtime.Exports.Exports(this);
+ }
+
+ ///
+ /// Instantiates a WebAssembly module for the given host.
+ ///
+ /// The host to use for the WebAssembly module's instance.
+ /// Returns a new .
+ public Instance Instantiate(IHost host)
+ {
+ if (host is null)
+ {
+ throw new ArgumentNullException(nameof(host));
+ }
+
+ if (host.Instance != null)
+ {
+ throw new InvalidOperationException("The host has already been associated with an instantiated module.");
+ }
+
+ host.Instance = new Instance(this, host);
+ return host.Instance;
+ }
+
+ ///
+ /// The associated with the module.
+ ///
+ public Store Store { get; private set; }
+
+ ///
+ /// The name of the module.
+ ///
+ public string Name { get; private set; }
+
+ ///
+ /// The imports of the module.
+ ///
+ public Wasmtime.Imports.Imports Imports { get; private set; }
+
+ ///
+ /// The exports of the module.
+ ///
+ ///
+ public Wasmtime.Exports.Exports Exports { get; private set; }
+
+ ///
+ public void Dispose()
+ {
+ if (!Handle.IsInvalid)
+ {
+ Handle.Dispose();
+ Handle.SetHandleAsInvalid();
+ }
+ }
+
+ internal Interop.ModuleHandle Handle { get; private set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/MutableGlobal.cs b/crates/misc/dotnet/src/MutableGlobal.cs
new file mode 100644
index 0000000000..223c8c5f0c
--- /dev/null
+++ b/crates/misc/dotnet/src/MutableGlobal.cs
@@ -0,0 +1,65 @@
+using System;
+
+namespace Wasmtime
+{
+ ///
+ /// Represents a mutable WebAssembly global value.
+ ///
+ public class MutableGlobal
+ {
+ ///
+ /// Creates a new with the given initial value.
+ ///
+ /// The initial value of the global.
+ public MutableGlobal(T initialValue)
+ {
+ InitialValue = initialValue;
+ Kind = Interop.ToValueKind(typeof(T));
+ }
+
+ ///
+ /// The value of the global.
+ ///
+ public T Value
+ {
+ get
+ {
+ if (Handle is null)
+ {
+ throw new InvalidOperationException("The global cannot be used before it is instantiated.");
+ }
+
+ unsafe
+ {
+ var v = stackalloc Interop.wasm_val_t[1];
+
+ Interop.wasm_global_get(Handle.DangerousGetHandle(), v);
+
+ // TODO: figure out a way that doesn't box the value
+ return (T)Interop.ToObject(v);
+ }
+ }
+ set
+ {
+ if (Handle is null)
+ {
+ throw new InvalidOperationException("The global cannot be used before it is instantiated.");
+ }
+
+ // TODO: figure out a way that doesn't box the value
+ var v = Interop.ToValue(value, Kind);
+
+ unsafe
+ {
+ Interop.wasm_global_set(Handle.DangerousGetHandle(), &v);
+ }
+ }
+ }
+
+ internal ValueKind Kind { get; private set; }
+
+ internal Interop.GlobalHandle Handle { get; set; }
+
+ internal T InitialValue { get; private set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/Store.cs b/crates/misc/dotnet/src/Store.cs
new file mode 100644
index 0000000000..37be522201
--- /dev/null
+++ b/crates/misc/dotnet/src/Store.cs
@@ -0,0 +1,75 @@
+using System;
+using System.IO;
+
+namespace Wasmtime
+{
+ ///
+ /// Represents the Wasmtime store.
+ ///
+ public sealed class Store : IDisposable
+ {
+ internal Store(Engine engine)
+ {
+ Handle = Interop.wasm_store_new(engine.Handle);
+
+ if (Handle.IsInvalid)
+ {
+ throw new WasmtimeException("Failed to create Wasmtime store.");
+ }
+ }
+
+ ///
+ /// Create a given the module name and bytes.
+ ///
+ /// The name of the module.
+ /// The bytes of the module.
+ /// Retuw .
+ public Module CreateModule(string name, byte[] bytes)
+ {
+ if (string.IsNullOrEmpty(name))
+ {
+ throw new ArgumentNullException(nameof(name));
+ }
+
+ if (bytes is null)
+ {
+ throw new ArgumentNullException(nameof(bytes));
+ }
+
+ return new Module(this, name, bytes);
+ }
+
+ ///
+ /// Create a given the module name and path to the WebAssembly file.
+ ///
+ /// The name of the module.
+ /// The path to the WebAssembly file.
+ /// Returns a new .
+ public Module CreateModule(string name, string path)
+ {
+ return CreateModule(name, File.ReadAllBytes(path));
+ }
+
+ ///
+ /// Create a given the path to the WebAssembly file.
+ ///
+ /// The path to the WebAssembly file.
+ /// Returns a new .
+ public Module CreateModule(string path)
+ {
+ return CreateModule(Path.GetFileNameWithoutExtension(path), path);
+ }
+
+ ///
+ public void Dispose()
+ {
+ if (!Handle.IsInvalid)
+ {
+ Handle.Dispose();
+ Handle.SetHandleAsInvalid();
+ }
+ }
+
+ internal Interop.StoreHandle Handle { get; private set; }
+ }
+}
diff --git a/crates/misc/dotnet/src/TrapException.cs b/crates/misc/dotnet/src/TrapException.cs
new file mode 100644
index 0000000000..795b4a397c
--- /dev/null
+++ b/crates/misc/dotnet/src/TrapException.cs
@@ -0,0 +1,41 @@
+using System;
+using System.Runtime.InteropServices;
+using System.Runtime.Serialization;
+
+namespace Wasmtime
+{
+ ///
+ /// The exception for WebAssembly traps.
+ ///
+ [Serializable]
+ public class TrapException : WasmtimeException
+ {
+ ///
+ public TrapException() { }
+
+ ///
+ public TrapException(string message) : base(message) { }
+
+ ///
+ public TrapException(string message, Exception inner) : base(message, inner) { }
+
+ ///
+ protected TrapException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+
+ internal static TrapException FromOwnedTrap(IntPtr trap)
+ {
+ unsafe
+ {
+ Interop.wasm_trap_message(trap, out var bytes);
+ var message = Marshal.PtrToStringUTF8((IntPtr)bytes.data, (int)bytes.size - 1 /* remove null */);
+ Interop.wasm_byte_vec_delete(ref bytes);
+
+ Interop.wasm_trap_delete(trap);
+
+ return new TrapException(message);
+ }
+ }
+
+ // TODO: expose trap frames
+ }
+}
diff --git a/crates/misc/dotnet/src/ValueKind.cs b/crates/misc/dotnet/src/ValueKind.cs
new file mode 100644
index 0000000000..cce415aee4
--- /dev/null
+++ b/crates/misc/dotnet/src/ValueKind.cs
@@ -0,0 +1,35 @@
+using System;
+
+namespace Wasmtime
+{
+ ///
+ /// Represents the possible kinds of WebAssembly values.
+ ///
+ public enum ValueKind
+ {
+ ///
+ /// The value is a 32-bit integer.
+ ///
+ Int32,
+ ///
+ /// The value is a 64-bit integer.
+ ///
+ Int64,
+ ///
+ /// The value is a 32-bit floating point number.
+ ///
+ Float32,
+ ///
+ /// The value is a 64-bit floating point number.
+ ///
+ Float64,
+ ///
+ /// The value is a reference.
+ ///
+ AnyRef = 128,
+ ///
+ /// The value is a function reference.
+ ///
+ FuncRef,
+ }
+}
diff --git a/crates/misc/dotnet/src/Wasmtime.csproj b/crates/misc/dotnet/src/Wasmtime.csproj
new file mode 100644
index 0000000000..ffb0054e9e
--- /dev/null
+++ b/crates/misc/dotnet/src/Wasmtime.csproj
@@ -0,0 +1,77 @@
+
+
+
+ netstandard2.1
+ true
+
+
+
+
+
+
+
+ Wasmtime.Dotnet
+ Wasmtime
+ $(WasmtimeVersion)-preview1
+ Peter Huene
+ Peter Huene
+ true
+ snupkg
+ https://github.com/bytecodealliance/wasmtime
+ Initial release of Wasmtime for .NET.
+ A .NET API for Wasmtime, a standalone WebAssembly runtime
+ webassembly, .net, wasm, wasmtime
+ Wasmtime
+
+A .NET API for Wasmtime.
+
+Wasmtime is a standalone runtime for WebAssembly, using the Cranelift JIT compiler.
+
+Wasmtime for .NET enables .NET code to instantiate WebAssembly modules and to interact with them in-process.
+
+
+
+
+
+ x86_64
+ https://github.com/bytecodealliance/wasmtime/releases/download/v$(WasmtimeVersion)/wasmtime-v$(WasmtimeVersion)-$(WasmtimeArchitecture)
+
+
+
+
+ $(ReleaseURLBase)-linux-c-api.tar.xz
+ $(IntermediateOutputPath)wasmtime-linux
+ linux.tar.xz
+ libwasmtime.so
+ runtimes/linux-x64/native
+
+
+ $(ReleaseURLBase)-macos-c-api.tar.xz
+ $(IntermediateOutputPath)wasmtime-macos
+ macos.tar.xz
+ libwasmtime.dylib
+ runtimes/osx-x64/native
+
+
+ $(ReleaseURLBase)-windows-c-api.zip
+ $(IntermediateOutputPath)wasmtime-windows
+ windows.zip
+ wasmtime.dll
+ runtimes/win-x64/native
+
+
+
+
+
+
+
+
+
+
+
+ %(WasmtimeDownload.PackagePath)
+
+
+
+
+
diff --git a/crates/misc/dotnet/src/WasmtimeException.cs b/crates/misc/dotnet/src/WasmtimeException.cs
new file mode 100644
index 0000000000..26f0e6c5e6
--- /dev/null
+++ b/crates/misc/dotnet/src/WasmtimeException.cs
@@ -0,0 +1,24 @@
+using System;
+using System.Runtime.Serialization;
+
+namespace Wasmtime
+{
+ ///
+ /// The base type for Wasmtime exceptions.
+ ///
+ [System.Serializable]
+ public class WasmtimeException : Exception
+ {
+ ///
+ public WasmtimeException() { }
+
+ ///
+ public WasmtimeException(string message) : base(message) { }
+
+ ///
+ public WasmtimeException(string message, Exception inner) : base(message, inner) { }
+
+ ///
+ protected WasmtimeException(SerializationInfo info, StreamingContext context) : base(info, context) { }
+ }
+}
diff --git a/crates/misc/dotnet/tests/Fixtures/ModuleFixture.cs b/crates/misc/dotnet/tests/Fixtures/ModuleFixture.cs
new file mode 100644
index 0000000000..91bd52bbad
--- /dev/null
+++ b/crates/misc/dotnet/tests/Fixtures/ModuleFixture.cs
@@ -0,0 +1,43 @@
+using System;
+using System.IO;
+using Wasmtime;
+
+namespace Wasmtime.Tests
+{
+ public abstract class ModuleFixture : IDisposable
+ {
+ public ModuleFixture()
+ {
+ Engine = new Engine();
+ Store = Engine.CreateStore();
+ Module = Store.CreateModule(Path.Combine("Modules", ModuleFileName));
+ }
+
+ public void Dispose()
+ {
+ if (Module != null)
+ {
+ Module.Dispose();
+ Module = null;
+ }
+
+ if (Store != null)
+ {
+ Store.Dispose();
+ Store = null;
+ }
+
+ if (Engine != null)
+ {
+ Engine.Dispose();
+ Engine = null;
+ }
+ }
+
+ public Engine Engine { get; set; }
+ public Store Store { get; set; }
+ public Module Module { get; set; }
+
+ protected abstract string ModuleFileName { get; }
+ }
+}
diff --git a/crates/misc/dotnet/tests/FunctionExportsTests.cs b/crates/misc/dotnet/tests/FunctionExportsTests.cs
new file mode 100644
index 0000000000..01d0929216
--- /dev/null
+++ b/crates/misc/dotnet/tests/FunctionExportsTests.cs
@@ -0,0 +1,150 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using FluentAssertions;
+using Xunit;
+
+namespace Wasmtime.Tests
+{
+ public class FunctionExportsFixture : ModuleFixture
+ {
+ protected override string ModuleFileName => "FunctionExports.wasm";
+ }
+
+ public class FunctionExportsTests : IClassFixture
+ {
+ public FunctionExportsTests(FunctionExportsFixture fixture)
+ {
+ Fixture = fixture;
+ }
+
+ private FunctionExportsFixture Fixture { get; set; }
+
+ [Theory]
+ [MemberData(nameof(GetFunctionExports))]
+ public void ItHasTheExpectedFunctionExports(string exportName, ValueKind[] expectedParameters, ValueKind[] expectedResults)
+ {
+ var export = Fixture.Module.Exports.Functions.Where(f => f.Name == exportName).FirstOrDefault();
+ export.Should().NotBeNull();
+ export.Parameters.Should().Equal(expectedParameters);
+ export.Results.Should().Equal(expectedResults);
+ }
+
+ [Fact]
+ public void ItHasTheExpectedNumberOfExportedFunctions()
+ {
+ GetFunctionExports().Count().Should().Be(Fixture.Module.Exports.Functions.Count);
+ }
+
+ public static IEnumerable