Remove the .NET implementation. (#1477)

This commit removes the .NET implementation from Wasmtime.

It now exists at https://github.com/bytecodealliance/wasmtime-dotnet.

Also updates the Wasmtime book to include information about using Wasmtime from
.NET.
This commit is contained in:
Peter Huene
2020-04-06 18:03:49 -07:00
committed by GitHub
parent bd374fd6fc
commit 9de0ab302c
96 changed files with 83 additions and 8193 deletions

View File

@@ -334,52 +334,6 @@ jobs:
name: bins-${{ matrix.os }} name: bins-${{ matrix.os }}
path: dist path: dist
# Build and test the .NET bindings
dotnet:
name: Test Wasmtime for .NET bindings
runs-on: ${{ matrix.os }}
strategy:
matrix:
build: [linux-debug, linux-release, macos-debug, macos-release, windows-debug, windows-release]
include:
- build: linux-debug
os: ubuntu-latest
config: debug
- build: linux-release
os: ubuntu-latest
config: release
- build: macos-debug
os: macos-latest
config: debug
- build: macos-release
os: macos-latest
config: release
- build: windows-debug
os: windows-latest
config: debug
- build: windows-release
os: windows-latest
config: release
steps:
- uses: actions/checkout@v1
with:
submodules: true
- uses: ./.github/actions/install-rust
- uses: ./.github/actions/binary-compatible-builds
- run: rustup target add wasm32-wasi
- uses: actions/setup-dotnet@v1
with:
dotnet-version: '3.0.101'
- name: Test
run: |
cd crates/misc/dotnet/tests
dotnet test -c ${{ matrix.config }}
- name: Create package
run: |
cd crates/misc/dotnet/src
dotnet pack -c ${{ matrix.config }}
if: matrix.os == 'macos-latest' # Currently the pack target only supports macOS
# Consumes all published artifacts from all the previous build steps, creates # Consumes all published artifacts from all the previous build steps, creates
# a bunch of tarballs for all of them, and then publishes the tarballs # a bunch of tarballs for all of them, and then publishes the tarballs
# themselves as an artifact (for inspection) and then optionally creates # themselves as an artifact (for inspection) and then optionally creates

View File

@@ -1,9 +0,0 @@
*.swp
*.*~
.DS_Store
.vs/
.vscode
bin/
obj/

View File

@@ -1,7 +0,0 @@
<Project>
<PropertyGroup>
<WasmtimeVersion>0.12.0</WasmtimeVersion>
<WasmtimeLibraryName>wasmtime</WasmtimeLibraryName>
<WasmtimePackageName>wasmtime-c-api</WasmtimePackageName>
</PropertyGroup>
</Project>

View File

@@ -1,12 +0,0 @@
<Project>
<PropertyGroup>
<LibraryPrefix Condition="'$(LibraryPrefix)' == '' and !$(NETCoreSdkRuntimeIdentifier.StartsWith('win'))">lib</LibraryPrefix>
<LibraryExtension Condition="'$(LibraryExtension)' == '' and $(NETCoreSdkRuntimeIdentifier.StartsWith('win'))">.dll</LibraryExtension>
<LibraryExtension Condition="'$(LibraryExtension)' == '' and $(NETCoreSdkRuntimeIdentifier.StartsWith('osx'))">.dylib</LibraryExtension>
<LibraryExtension Condition="'$(LibraryExtension)' == ''">.so</LibraryExtension>
<WasmtimeLibraryFilename>$(LibraryPrefix)$(WasmtimeLibraryName)$(LibraryExtension)</WasmtimeLibraryFilename>
<WasmtimeOutputPath>$(MSBuildThisFileDirectory)../../../target/$(Configuration.ToLower())</WasmtimeOutputPath>
<BuildWasmtimeCommand Condition="'$(Configuration)' == 'Release'">cargo build --release -p $(WasmtimePackageName)</BuildWasmtimeCommand>
<BuildWasmtimeCommand Condition="'$(BuildWasmtimeCommand)' == ''">cargo build -p $(WasmtimePackageName)</BuildWasmtimeCommand>
</PropertyGroup>
</Project>

View File

@@ -1,219 +0,0 @@
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.
--- LLVM Exceptions to the Apache 2.0 License ----
As an exception, if, as a result of your compiling your source code, portions
of this Software are embedded into an Object form of such source code, you
may redistribute such embedded portions in such Object form without complying
with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
In addition, if you combine or link compiled forms of this Software with
software that is licensed under the GPLv2 ("Combined Software") and if a
court of competent jurisdiction determines that the patent provision (Section
3), the indemnity provision (Section 9) or other Section of the License
conflicts with the conditions of the GPLv2, you may retroactively and
prospectively choose to deem waived or otherwise exclude such Section(s) of
the License, but only in their entirety and only with respect to the Combined
Software.

View File

@@ -1,108 +0,0 @@
# 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.<version>.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 | ✅ |

View File

@@ -1,31 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.29519.181
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasmtime", "src\Wasmtime.csproj", "{5EB63C51-5286-4DDF-BF7F-4110CC6D80B8}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Wasmtime.Tests", "tests\Wasmtime.Tests.csproj", "{8A200114-1D0B-4F90-9F82-1FFE47C207DD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{5EB63C51-5286-4DDF-BF7F-4110CC6D80B8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{5EB63C51-5286-4DDF-BF7F-4110CC6D80B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
{5EB63C51-5286-4DDF-BF7F-4110CC6D80B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
{5EB63C51-5286-4DDF-BF7F-4110CC6D80B8}.Release|Any CPU.Build.0 = Release|Any CPU
{8A200114-1D0B-4F90-9F82-1FFE47C207DD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8A200114-1D0B-4F90-9F82-1FFE47C207DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8A200114-1D0B-4F90-9F82-1FFE47C207DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8A200114-1D0B-4F90-9F82-1FFE47C207DD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F5AC35E5-1373-49E6-97DC-68CB5E0369E0}
EndGlobalSection
EndGlobal

View File

@@ -1,9 +0,0 @@
###############
# folder #
###############
/**/DROP/
/**/TEMP/
/**/packages/
/**/bin/
/**/obj/
_site

View File

@@ -1,5 +0,0 @@
###############
# temp file #
###############
*.yml
.manifest

View File

@@ -1,5 +0,0 @@
# 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.

View File

@@ -1,205 +0,0 @@
# 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.14.0-preview1 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 Program
{
static void Main(string[] args)
{
using var host = new Host();
host.DefineFunction(
"env",
"print",
(Caller caller, int address, int length) => {
Console.WriteLine(caller.GetMemory("mem").ReadString(address, length));
}
);
using var module = host.LoadModule("hello.wasm");
using dynamic instance = host.Instantiate(module);
instance.run();
}
}
}
```
The `Host` class is responsible for implementing an environment that WebAssembly modules can execute in.
Here we are creating a host that is implementing a function named `print` in the `env` module, which is the default import module name for WebAssembly modules compiled using the Rust toolchain.
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 = host.Instantiate(module);
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_.

View File

@@ -1,2 +0,0 @@
- name: Introduction to Wasmtime for .NET
href: intro.md

View File

@@ -1,72 +0,0 @@
{
"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"
}
}
}
}

View File

@@ -1,7 +0,0 @@
# 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.

View File

@@ -1,21 +0,0 @@
{{!Copyright (c) Oscar Vasquez. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}}
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}</title>
<meta name="viewport" content="width=device-width">
<meta name="title" content="{{#title}}{{title}}{{/title}}{{^title}}{{>partials/title}}{{/title}} {{#_appTitle}}| {{_appTitle}} {{/_appTitle}}">
<meta name="generator" content="docfx {{_docfxVersion}}">
{{#_description}}<meta name="description" content="{{_description}}">{{/_description}}
<link rel="shortcut icon" href="{{_rel}}{{{_appFaviconPath}}}{{^_appFaviconPath}}favicon.ico{{/_appFaviconPath}}">
<link rel="stylesheet" href="{{_rel}}styles/docfx.vendor.css">
<link rel="stylesheet" href="{{_rel}}styles/docfx.css">
<link rel="stylesheet" href="{{_rel}}styles/main.css">
<link href="https://fonts.googleapis.com/css?family=Open+Sans" rel="stylesheet">
<meta property="docfx:navrel" content="{{_navRel}}">
<meta property="docfx:tocrel" content="{{_tocRel}}">
{{#_noindex}}<meta name="searchOption" content="noindex">{{/_noindex}}
{{#_enableSearch}}<meta property="docfx:rel" content="{{_rel}}">{{/_enableSearch}}
{{#_enableNewTab}}<meta property="docfx:newtab" content="true">{{/_enableNewTab}}
</head>

View File

@@ -1,394 +0,0 @@
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;
}

View File

@@ -1,7 +0,0 @@
- name: Articles
href: articles/
- name: API Documentation
href: api/
homepage: api/index.md
- name: GitHub Repo
href: https://github.com/bytecodealliance/wasmtime

View File

@@ -1,28 +0,0 @@
using System;
using Wasmtime;
namespace HelloExample
{
class Program
{
static void Main(string[] args)
{
using var host = new Host();
var global = host.DefineMutableGlobal("", "global", 1);
host.DefineFunction(
"",
"print_global",
() => {
Console.WriteLine($"The value of the global is: {global.Value}.");
}
);
using var module = host.LoadModule("global.wasm");
using dynamic instance = host.Instantiate(module);
instance.run(20);
}
}
}

View File

@@ -1,21 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Wasmtime.csproj" />
</ItemGroup>
<!-- This is needed as we're not referencing Wasmtime as a package. -->
<Target Name="BuildWasmtime" BeforeTargets="AssignTargetPaths">
<Message Text="Building Wasmtime from source." Importance="High" />
<Exec Command="$(BuildWasmtimeCommand)" StandardOutputImportance="Low" StandardErrorImportance="Low" />
<ItemGroup>
<None Include="$(WasmtimeOutputPath)/$(WasmtimeLibraryFilename)" Link="$(WasmtimeLibraryFilename)" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Target>
</Project>

View File

@@ -1,22 +0,0 @@
(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))
)

View File

@@ -1,24 +0,0 @@
using System;
using Wasmtime;
namespace HelloExample
{
class Program
{
static void Main(string[] args)
{
using var host = new Host();
host.DefineFunction(
"",
"hello",
() => Console.WriteLine("Hello from C#, WebAssembly!")
);
using var module = host.LoadModule("hello.wasm");
using dynamic instance = host.Instantiate(module);
instance.run();
}
}
}

View File

@@ -1,21 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Wasmtime.csproj" />
</ItemGroup>
<!-- This is needed as we're not referencing Wasmtime as a package. -->
<Target Name="BuildWasmtime" BeforeTargets="AssignTargetPaths">
<Message Text="Building Wasmtime from source." Importance="High" />
<Exec Command="$(BuildWasmtimeCommand)" StandardOutputImportance="Low" StandardErrorImportance="Low" />
<ItemGroup>
<None Include="$(WasmtimeOutputPath)/$(WasmtimeLibraryFilename)" Link="$(WasmtimeLibraryFilename)" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Target>
</Project>

View File

@@ -1,8 +0,0 @@
(module
(type $t0 (func))
(import "" "hello" (func $.hello (type $t0)))
(func $run
call $.hello
)
(export "run" (func $run))
)

View File

@@ -1,27 +0,0 @@
using System;
using Wasmtime;
namespace HelloExample
{
class Program
{
static void Main(string[] args)
{
using var host = new Host();
host.DefineFunction(
"",
"log",
(Caller caller, int address, int length) => {
var message = caller.GetMemory("mem").ReadString(address, length);
Console.WriteLine($"Message from WebAssembly: {message}");
}
);
using var module = host.LoadModule("memory.wasm");
using dynamic instance = host.Instantiate(module);
instance.run();
}
}
}

View File

@@ -1,21 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Wasmtime.csproj" />
</ItemGroup>
<!-- This is needed as we're not referencing Wasmtime as a package. -->
<Target Name="BuildWasmtime" BeforeTargets="AssignTargetPaths">
<Message Text="Building Wasmtime from source." Importance="High" />
<Exec Command="$(BuildWasmtimeCommand)" StandardOutputImportance="Low" StandardErrorImportance="Low" />
<ItemGroup>
<None Include="$(WasmtimeOutputPath)/$(WasmtimeLibraryFilename)" Link="$(WasmtimeLibraryFilename)" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Target>
</Project>

View File

@@ -1,12 +0,0 @@
(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))
)

View File

@@ -1,82 +0,0 @@
using System;
using System.Text;
namespace Wasmtime
{
/// <summary>
/// Represents an exported memory of a host function caller.
/// </summary>
public class CallerMemory : MemoryBase, IDisposable
{
/// <inheritdoc/>
public void Dispose()
{
if (!_extern.IsInvalid)
{
_extern.Dispose();
_extern.SetHandleAsInvalid();
}
}
internal CallerMemory(Interop.ExternHandle ext, IntPtr memory)
{
_extern = ext;
_memory = memory;
}
/// <inheritdoc/>
protected override IntPtr MemoryHandle => _memory;
private Interop.ExternHandle _extern;
private IntPtr _memory;
}
/// <summary>
/// Represents information of the caller of a host function.
/// </summary>
public class Caller
{
/// <summary>
/// Gets an exported memory of the caller by the given name.
/// </summary>
/// <param name="name">The name of the exported memory.</param>
/// <returns>Returns the exported memory if found or null if a memory of the requested name is not exported.</returns>
public CallerMemory GetMemory(string name)
{
if (Handle == IntPtr.Zero)
{
throw new InvalidOperationException();
}
unsafe
{
var bytes = Encoding.UTF8.GetBytes(name);
fixed (byte* ptr = bytes)
{
Interop.wasm_byte_vec_t nameVec = new Interop.wasm_byte_vec_t();
nameVec.size = (UIntPtr)bytes.Length;
nameVec.data = ptr;
var export = Interop.wasmtime_caller_export_get(Handle, ref nameVec);
if (export.IsInvalid)
{
return null;
}
var memory = Interop.wasm_extern_as_memory(export.DangerousGetHandle());
if (memory == IntPtr.Zero)
{
export.Dispose();
return null;
}
return new CallerMemory(export, memory);
}
}
}
internal IntPtr Handle { get; set; }
}
}

View File

@@ -1,31 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace Wasmtime.Exports
{
/// <summary>
/// Represents an export of a WebAssembly module.
/// </summary>
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);
}
}
/// <summary>
/// The name of the export.
/// </summary>
public string Name { get; private set; }
/// <inheritdoc/>
public override string ToString()
{
return Name;
}
}
}

View File

@@ -1,97 +0,0 @@
using System;
using System.Collections.Generic;
namespace Wasmtime.Exports
{
/// <summary>
/// Represents the exports of a WebAssembly module.
/// </summary>
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<Export>((int)exports.size);
var functions = new List<FunctionExport>();
var globals = new List<GlobalExport>();
var tables = new List<TableExport>();
var memories = new List<MemoryExport>();
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);
}
}
/// <summary>
/// The exported functions of a WebAssembly module.
/// </summary>
public IReadOnlyList<FunctionExport> Functions { get; private set; }
/// <summary>
/// The exported globals of a WebAssembly module.
/// </summary>
public IReadOnlyList<GlobalExport> Globals { get; private set; }
/// <summary>
/// The exported tables of a WebAssembly module.
/// </summary>
public IReadOnlyList<TableExport> Tables { get; private set; }
/// <summary>
/// The exported memories of a WebAssembly module.
/// </summary>
public IReadOnlyList<MemoryExport> Memories { get; private set; }
internal List<Export> All { get; private set; }
}
}

View File

@@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Wasmtime.Exports
{
/// <summary>
/// Represents a function exported from a WebAssembly module.
/// </summary>
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));
}
}
/// <summary>
/// The parameter of the exported WebAssembly function.
/// </summary>
public IReadOnlyList<ValueKind> Parameters { get; private set; }
/// <summary>
/// The results of the exported WebAssembly function.
/// </summary>
public IReadOnlyList<ValueKind> Results { get; private set; }
}
}

View File

@@ -1,30 +0,0 @@
using System;
using System.Diagnostics;
namespace Wasmtime.Exports
{
/// <summary>
/// Represents a global variable exported from a WebAssembly module.
/// </summary>
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;
}
/// <summary>
/// The kind of value for the global variable.
/// </summary>
public ValueKind Kind { get; private set; }
/// <summary>
/// Determines whether or not the global variable is mutable.
/// </summary>
public bool IsMutable { get; private set; }
}
}

View File

@@ -1,35 +0,0 @@
using System;
using System.Diagnostics;
namespace Wasmtime.Exports
{
/// <summary>
/// Represents a memory exported from a WebAssembly module.
/// </summary>
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;
}
}
/// <summary>
/// The minimum memory size (in WebAssembly page units).
/// </summary>
public uint Minimum { get; private set; }
/// <summary>
/// The maximum memory size (in WebAssembly page units).
/// </summary>
public uint Maximum { get; private set; }
}
}

View File

@@ -1,42 +0,0 @@
using System;
using System.Diagnostics;
namespace Wasmtime.Exports
{
/// <summary>
/// Represents a table exported from a WebAssembly module.
/// </summary>
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;
}
}
/// <summary>
/// The value kind of the table.
/// </summary>
public ValueKind Kind { get; private set; }
/// <summary>
/// The minimum number of elements in the table.
/// </summary>
public uint Minimum { get; private set; }
/// <summary>
/// The maximum number of elements in the table.
/// </summary>
public uint Maximum { get; private set; }
}
}

View File

@@ -1,87 +0,0 @@
using System;
using System.Collections.Generic;
using Wasmtime.Exports;
namespace Wasmtime.Externs
{
/// <summary>
/// Represents an external (instantiated) WebAssembly function.
/// </summary>
public class ExternFunction
{
internal ExternFunction(FunctionExport export, IntPtr func)
{
_export = export;
_func = func;
}
/// <summary>
/// The name of the WebAssembly function.
/// </summary>
public string Name => _export.Name;
/// <summary>
/// The parameters of the WebAssembly function.
/// </summary>
public IReadOnlyList<ValueKind> Parameters => _export.Parameters;
/// <summary>
/// The results of the WebAssembly function.
/// </summary>
public IReadOnlyList<ValueKind> Results => _export.Results;
/// <summary>
/// Invokes the WebAssembly function.
/// </summary>
/// <param name="arguments">The array of arguments to pass to the function.</param>
/// <returns>
/// 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.
/// </returns>
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;
}
}

View File

@@ -1,62 +0,0 @@
using System;
using Wasmtime.Exports;
namespace Wasmtime.Externs
{
/// <summary>
/// Represents an external (instantiated) WebAssembly global.
/// </summary>
public class ExternGlobal
{
internal ExternGlobal(GlobalExport export, IntPtr global)
{
_export = export;
_global = global;
}
/// <summary>
/// The name of the WebAssembly global.
/// </summary>
public string Name => _export.Name;
/// <summary>
/// The kind of value for the global variable.
/// </summary>
public ValueKind Kind => _export.Kind;
/// <summary>
/// Determines whether or not the global variable is mutable.
/// </summary>
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;
}
}

View File

@@ -1,39 +0,0 @@
using System;
using System.Buffers.Binary;
using System.Text;
using Wasmtime.Exports;
namespace Wasmtime.Externs
{
/// <summary>
/// Represents an external (instantiated) WebAssembly memory.
/// </summary>
public class ExternMemory : MemoryBase
{
internal ExternMemory(MemoryExport export, IntPtr memory)
{
_export = export;
_memory = memory;
}
/// <summary>
/// The name of the WebAssembly memory.
/// </summary>
public string Name => _export.Name;
/// <summary>
/// The minimum memory size (in WebAssembly page units).
/// </summary>
public uint Minimum => _export.Minimum;
/// <summary>
/// The maximum memory size (in WebAssembly page units).
/// </summary>
public uint Maximum => _export.Maximum;
protected override IntPtr MemoryHandle => _memory;
private MemoryExport _export;
private IntPtr _memory;
}
}

View File

@@ -1,67 +0,0 @@
using System;
using System.Collections.Generic;
using Wasmtime.Exports;
namespace Wasmtime.Externs
{
/// <summary>
/// Represents external (instantiated) WebAssembly functions, globals, tables, and memories.
/// </summary>
public class Externs
{
internal Externs(Wasmtime.Exports.Exports exports, Interop.wasm_extern_vec_t externs)
{
var functions = new List<ExternFunction>();
var globals = new List<ExternGlobal>();
var memories = new List<ExternMemory>();
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;
}
/// <summary>
/// The extern functions from an instantiated WebAssembly module.
/// </summary>
public IReadOnlyList<ExternFunction> Functions { get; private set; }
/// <summary>
/// The extern globals from an instantiated WebAssembly module.
/// </summary>
public IReadOnlyList<ExternGlobal> Globals { get; private set; }
/// <summary>
/// The extern memories from an instantiated WebAssembly module.
/// </summary>
public IReadOnlyList<ExternMemory> Memories { get; private set; }
}
}

View File

@@ -1,355 +0,0 @@
using System;
using System.Linq;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
namespace Wasmtime
{
/// <summary>
/// Represents a host function.
/// </summary>
public class Function : IDisposable
{
/// <inheritdoc/>
public void Dispose()
{
if (!Handle.IsInvalid)
{
Handle.Dispose();
Handle.SetHandleAsInvalid();
}
}
internal Function(Interop.StoreHandle store, Delegate func, bool hasReturn)
{
if (func is null)
{
throw new ArgumentNullException(nameof(func));
}
var type = func.GetType();
Span<Type> parameterTypes = null;
Type returnType = null;
if (hasReturn)
{
parameterTypes = type.GenericTypeArguments[0..^1];
returnType = type.GenericTypeArguments[^1];
}
else
{
parameterTypes = type.GenericTypeArguments;
returnType = null;
}
bool hasCaller = parameterTypes.Length > 0 && parameterTypes[0] == typeof(Caller);
if (hasCaller)
{
parameterTypes = parameterTypes[1..];
}
ValidateParameterTypes(parameterTypes);
ValidateReturnType(returnType);
var parameters = CreateValueTypeVec(parameterTypes);
var results = CreateReturnValueTypeVec(returnType);
using var funcType = Interop.wasm_functype_new(ref parameters, ref results);
if (hasCaller)
{
Callback = CreateCallbackWithCaller(store, func, parameterTypes.Length, hasReturn);
Handle = Interop.wasmtime_func_new(store, funcType, (Interop.WasmtimeFuncCallback)Callback);
}
else
{
Callback = CreateCallback(store, func, parameterTypes.Length, hasReturn);
Handle = Interop.wasm_func_new(store, funcType, (Interop.WasmFuncCallback)Callback);
}
if (Handle.IsInvalid)
{
throw new WasmtimeException("Failed to create Wasmtime function.");
}
}
private static void ValidateParameterTypes(Span<Type> parameters)
{
foreach (var type in parameters)
{
if (type == typeof(Caller))
{
throw new WasmtimeException($"A Caller parameter must be the first parameter of the function.");
}
if (!Interop.TryGetValueKind(type, out var kind))
{
throw new WasmtimeException($"Unable to create a function with parameter of type '{type.ToString()}'.");
}
}
}
private static void ValidateReturnType(Type returnType)
{
if (returnType is null)
{
return;
}
if (IsTuple(returnType))
{
var types = returnType
.GetGenericArguments()
.SelectMany(type =>
{
if (type.IsConstructedGenericType)
{
return type.GenericTypeArguments;
}
return Enumerable.Repeat(type, 1);
});
foreach (var type in types)
{
ValidateReturnType(type);
}
return;
}
if (!Interop.TryGetValueKind(returnType, out var kind))
{
throw new WasmtimeException($"Unable to create a function with a return type of type '{returnType.ToString()}'.");
}
}
private static bool IsTuple(Type type)
{
if (!type.IsConstructedGenericType)
{
return false;
}
var definition = type.GetGenericTypeDefinition();
return definition == typeof(ValueTuple) ||
definition == typeof(ValueTuple<>) ||
definition == typeof(ValueTuple<,>) ||
definition == typeof(ValueTuple<,,>) ||
definition == typeof(ValueTuple<,,,>) ||
definition == typeof(ValueTuple<,,,,>) ||
definition == typeof(ValueTuple<,,,,,>) ||
definition == typeof(ValueTuple<,,,,,,>) ||
definition == typeof(ValueTuple<,,,,,,,>);
}
private static unsafe Interop.WasmFuncCallback CreateCallback(Interop.StoreHandle store, Delegate func, int parameterCount, bool hasReturn)
{
// NOTE: this capture is not thread-safe.
var args = new object[parameterCount];
var method = func.Method;
var target = func.Target;
return (arguments, results) =>
{
try
{
SetArgs(arguments, args);
var result = method.Invoke(target, BindingFlags.DoNotWrapExceptions, null, args, null);
if (hasReturn)
{
SetResults(result, results);
}
return IntPtr.Zero;
}
catch (Exception ex)
{
var bytes = Encoding.UTF8.GetBytes(ex.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(store, ref message);
}
}
};
}
private static unsafe Interop.WasmtimeFuncCallback CreateCallbackWithCaller(Interop.StoreHandle store, Delegate func, int parameterCount, bool hasReturn)
{
// NOTE: this capture is not thread-safe.
var args = new object[parameterCount + 1];
var caller = new Caller();
var method = func.Method;
var target = func.Target;
args[0] = caller;
return (callerHandle, arguments, results) =>
{
try
{
caller.Handle = callerHandle;
SetArgs(arguments, args, 1);
var result = method.Invoke(target, BindingFlags.DoNotWrapExceptions, null, args, null);
caller.Handle = IntPtr.Zero;
if (hasReturn)
{
SetResults(result, results);
}
return IntPtr.Zero;
}
catch (Exception ex)
{
var bytes = Encoding.UTF8.GetBytes(ex.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(store, ref message);
}
}
};
}
private static unsafe void SetArgs(Interop.wasm_val_t* arguments, object[] args, int offset = 0)
{
for (int i = 0; i < args.Length - offset; ++i)
{
var arg = arguments[i];
switch (arg.kind)
{
case Interop.wasm_valkind_t.WASM_I32:
args[i + offset] = arg.of.i32;
break;
case Interop.wasm_valkind_t.WASM_I64:
args[i + offset] = arg.of.i64;
break;
case Interop.wasm_valkind_t.WASM_F32:
args[i + offset] = arg.of.f32;
break;
case Interop.wasm_valkind_t.WASM_F64:
args[i + offset] = 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)
{
SetResult(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 static Interop.wasm_valtype_vec_t CreateValueTypeVec(Span<Type> types)
{
Interop.wasm_valtype_vec_t vec;
Interop.wasm_valtype_vec_new_uninitialized(out vec, (UIntPtr)types.Length);
int i = 0;
foreach (var type in types)
{
var valType = Interop.wasm_valtype_new((Interop.wasm_valkind_t)Interop.ToValueKind(type));
unsafe
{
vec.data[i++] = valType.DangerousGetHandle();
}
valType.SetHandleAsInvalid();
}
return vec;
}
private static Interop.wasm_valtype_vec_t CreateReturnValueTypeVec(Type returnType)
{
if (returnType is null)
{
Interop.wasm_valtype_vec_t vec;
Interop.wasm_valtype_vec_new_empty(out vec);
return vec;
}
if (IsTuple(returnType))
{
return CreateValueTypeVec(
returnType
.GetGenericArguments()
.SelectMany(type =>
{
if (type.IsConstructedGenericType)
{
return type.GenericTypeArguments;
}
return Enumerable.Repeat(type, 1);
})
.ToArray()
);
}
return CreateValueTypeVec(new Type[] { returnType });
}
internal Interop.FunctionHandle Handle { get; private set; }
internal Delegate Callback { get; private set; }
}
}

View File

@@ -1,82 +0,0 @@
using System;
namespace Wasmtime
{
/// <summary>
/// Represents a constant WebAssembly global value.
/// </summary>
public class Global<T> : IDisposable
{
/// <summary>
/// The value of the global.
/// </summary>
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);
return (T)Interop.ToObject(v);
}
}
}
/// <summary>
/// Gets the value kind of the global.
/// </summary>
/// <value></value>
public ValueKind Kind { get; private set; }
/// <inheritdoc/>
public void Dispose()
{
if (!Handle.IsInvalid)
{
Handle.Dispose();
Handle.SetHandleAsInvalid();
}
}
internal Global(Interop.StoreHandle store, T initialValue)
{
if (!Interop.TryGetValueKind(typeof(T), out var kind))
{
throw new WasmtimeException($"Global variables cannot be of type '{typeof(T).ToString()}'.");
}
Kind = kind;
var value = Interop.ToValue((object)initialValue, Kind);
var valueType = Interop.wasm_valtype_new(value.kind);
var valueTypeHandle = valueType.DangerousGetHandle();
valueType.SetHandleAsInvalid();
using var globalType = Interop.wasm_globaltype_new(
valueTypeHandle,
Interop.wasm_mutability_t.WASM_CONST
);
unsafe
{
Handle = Interop.wasm_global_new(store, globalType, &value);
if (Handle.IsInvalid)
{
throw new WasmtimeException("Failed to create Wasmtime global.");
}
}
}
internal Interop.GlobalHandle Handle { get; set; }
}
}

View File

@@ -1,809 +0,0 @@
using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Wasmtime
{
/// <summary>
/// Represents a WebAssembly host environment.
/// </summary>
/// <remarks>
/// A host is used to configure the environment for WebAssembly modules to execute in.
/// </remarks>
public class Host : IDisposable
{
/// <summary>
/// Constructs a new host.
/// </summary>
public Host()
{
Initialize(Interop.wasm_engine_new());
}
/// <summary>
/// Defines a WASI implementation in the host.
/// </summary>
/// <param name="name">The name of the WASI module to define.</param>
/// <param name="config">The <see cref="WasiConfiguration"/> to configure the WASI implementation with.</param>
public void DefineWasi(string name, WasiConfiguration config = null)
{
CheckDisposed();
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException("Name cannot be null or empty.", nameof(name));
}
if (config is null)
{
config = new WasiConfiguration();
}
using var wasi = config.CreateWasi(Store, name);
var error = Interop.wasmtime_linker_define_wasi(Linker, wasi);
if (error != IntPtr.Zero)
{
throw WasmtimeException.FromOwnedError(error);
}
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction(string moduleName, string name, Action func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T>(string moduleName, string name, Action<T> func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2>(string moduleName, string name, Action<T1, T2> func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3>(string moduleName, string name, Action<T1, T2, T3> func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4>(string moduleName, string name, Action<T1, T2, T3, T4> func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5>(string moduleName, string name, Action<T1, T2, T3, T4, T5> func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6>(string moduleName, string name, Action<T1, T2, T3, T4, T5, T6> func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7>(string moduleName, string name, Action<T1, T2, T3, T4, T5, T6, T7> func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8>(string moduleName, string name, Action<T1, T2, T3, T4, T5, T6, T7, T8> func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9>(string moduleName, string name, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9> func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(string moduleName, string name, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(string moduleName, string name, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(string moduleName, string name, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(string moduleName, string name, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(string moduleName, string name, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(string moduleName, string name, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(string moduleName, string name, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> func)
{
return DefineFunction(moduleName, name, func, false);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<TResult>(string moduleName, string name, Func<TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T, TResult>(string moduleName, string name, Func<T, TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, TResult>(string moduleName, string name, Func<T1, T2, TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, TResult>(string moduleName, string name, Func<T1, T2, T3, TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, TResult>(string moduleName, string name, Func<T1, T2, T3, T4, TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, TResult>(string moduleName, string name, Func<T1, T2, T3, T4, T5, TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, TResult>(string moduleName, string name, Func<T1, T2, T3, T4, T5, T6, TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, TResult>(string moduleName, string name, Func<T1, T2, T3, T4, T5, T6, T7, TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, TResult>(string moduleName, string name, Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult>(string moduleName, string name, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult>(string moduleName, string name, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult>(string moduleName, string name, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult>(string moduleName, string name, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult>(string moduleName, string name, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult>(string moduleName, string name, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult>(string moduleName, string name, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a host function.
/// </summary>
/// <param name="moduleName">The module name of the host function.</param>
/// <param name="name">The name of the host function.</param>
/// <param name="func">The callback for when the host function is invoked.</param>
/// <returns>Returns a <see cref="Function"/> representing the host function.</returns>
public Function DefineFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult>(string moduleName, string name, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, TResult> func)
{
return DefineFunction(moduleName, name, func, true);
}
/// <summary>
/// Defines a new host global variable.
/// </summary>
/// <param name="moduleName">The module name of the host variable.</param>
/// <param name="name">The name of the host variable.</param>
/// <param name="initialValue">The initial value of the host variable.</param>
/// <typeparam name="T">The type of the host variable.</typeparam>
/// <returns>Returns a new <see cref="Global"/> representing the defined global variable.</returns>
public Global<T> DefineGlobal<T>(string moduleName, string name, T initialValue)
{
CheckDisposed();
if (moduleName is null)
{
throw new ArgumentNullException(nameof(moduleName));
}
if (name is null)
{
throw new ArgumentNullException(nameof(name));
}
var global = new Global<T>(Store, initialValue);
if (!Define(moduleName, name, Interop.wasm_global_as_extern(global.Handle)))
{
global.Dispose();
throw new WasmtimeException($"Failed to define global '{name}' in module '{moduleName}'.");
}
return global;
}
/// <summary>
/// Defines a new host mutable global variable.
/// </summary>
/// <param name="moduleName">The module name of the host variable.</param>
/// <param name="name">The name of the host variable.</param>
/// <param name="initialValue">The initial value of the host variable.</param>
/// <typeparam name="T">The type of the host variable.</typeparam>
/// <returns>Returns a new <see cref="MutableGlobal"/> representing the defined mutable global variable.</returns>
public MutableGlobal<T> DefineMutableGlobal<T>(string moduleName, string name, T initialValue)
{
CheckDisposed();
if (moduleName is null)
{
throw new ArgumentNullException(nameof(moduleName));
}
if (name is null)
{
throw new ArgumentNullException(nameof(name));
}
var global = new MutableGlobal<T>(Store, initialValue);
if (!Define(moduleName, name, Interop.wasm_global_as_extern(global.Handle)))
{
global.Dispose();
throw new WasmtimeException($"Failed to define global '{name}' in module '{moduleName}'.");
}
return global;
}
/// <summary>
/// Defines a new host memory.
/// </summary>
/// <param name="moduleName">The module name of the host memory.</param>
/// <param name="name">The name of the host memory.</param>
/// <param name="minimum">The minimum number of pages for the host memory.</param>
/// <param name="maximum">The maximum number of pages for the host memory.</param>
/// <returns>Returns a new <see cref="Memory"/> representing the defined memory.</returns>
public Memory DefineMemory(string moduleName, string name, uint minimum = 1, uint maximum = uint.MaxValue)
{
CheckDisposed();
if (moduleName is null)
{
throw new ArgumentNullException(nameof(moduleName));
}
if (name is null)
{
throw new ArgumentNullException(nameof(name));
}
var memory = new Memory(Store, minimum, maximum);
if (!Define(moduleName, name, Interop.wasm_memory_as_extern(memory.Handle)))
{
memory.Dispose();
throw new WasmtimeException($"Failed to define memory '{name}' in module '{moduleName}'.");
}
return memory;
}
/// <summary>
/// Loads a <see cref="Module"/> given the module name and bytes.
/// </summary>
/// <param name="name">The name of the module.</param>
/// <param name="bytes">The bytes of the module.</param>
/// <returns>Returns a new <see cref="Module"/>.</returns>
public Module LoadModule(string name, byte[] bytes)
{
CheckDisposed();
if (string.IsNullOrEmpty(name))
{
throw new ArgumentNullException(nameof(name));
}
if (bytes is null)
{
throw new ArgumentNullException(nameof(bytes));
}
return new Module(Store, name, bytes);
}
/// <summary>
/// Loads a <see cref="Module"/> given the path to the WebAssembly file.
/// </summary>
/// <param name="path">The path to the WebAssembly file.</param>
/// <returns>Returns a new <see cref="Module"/>.</returns>
public Module LoadModule(string path)
{
return LoadModule(Path.GetFileNameWithoutExtension(path), File.ReadAllBytes(path));
}
/// <summary>
/// Loads a <see cref="Module"/> based on a WebAssembly text format representation.
/// </summary>
/// <param name="name">The name of the module.</param>
/// <param name="text">The WebAssembly text format representation of the module.</param>
/// <returns>Returns a new <see cref="Module"/>.</returns>
public Module LoadModuleText(string name, string text)
{
CheckDisposed();
if (string.IsNullOrEmpty(name))
{
throw new ArgumentNullException(nameof(name));
}
if (text is null)
{
throw new ArgumentNullException(nameof(text));
}
var textBytes = Encoding.UTF8.GetBytes(text);
unsafe
{
fixed (byte *ptr = textBytes)
{
Interop.wasm_byte_vec_t textVec;
textVec.size = (UIntPtr)textBytes.Length;
textVec.data = ptr;
var error = Interop.wasmtime_wat2wasm(ref textVec, out var bytes);
if (error != IntPtr.Zero)
{
throw WasmtimeException.FromOwnedError(error);
}
var byteSpan = new ReadOnlySpan<byte>(bytes.data, checked((int)bytes.size));
var moduleBytes = byteSpan.ToArray();
Interop.wasm_byte_vec_delete(ref bytes);
return LoadModule(name, moduleBytes);
}
}
}
/// <summary>
/// Loads a <see cref="Module"/> based on the path to a WebAssembly text format file.
/// </summary>
/// <param name="path">The path to the WebAssembly text format file.</param>
/// <returns>Returns a new <see cref="Module"/>.</returns>
public Module LoadModuleText(string path)
{
return LoadModuleText(Path.GetFileNameWithoutExtension(path), File.ReadAllText(path));
}
/// <summary>
/// Instantiates a WebAssembly module.
/// </summary>
/// <param name="module">The module to instantiate.</param>
/// <returns>Returns a new <see cref="Instance" />.</returns>
public Instance Instantiate(Module module)
{
CheckDisposed();
if (module is null)
{
throw new ArgumentNullException(nameof(module));
}
return new Instance(Linker, module);
}
/// <summary>
/// Clears all existing definitions in the host.
/// </summary>
public void ClearDefinitions()
{
CheckDisposed();
var linker = Interop.wasmtime_linker_new(Store);
if (linker.IsInvalid)
{
throw new WasmtimeException("Failed to create Wasmtime linker.");
}
Interop.wasmtime_linker_allow_shadowing(linker, allowShadowing: true);
Linker.Dispose();
Linker = linker;
}
/// <inheritdoc/>
public void Dispose()
{
if (!Linker.IsInvalid)
{
Linker.Dispose();
Linker.SetHandleAsInvalid();
}
if (!Store.IsInvalid)
{
Store.Dispose();
Store.SetHandleAsInvalid();
}
if (!Engine.IsInvalid)
{
Engine.Dispose();
Engine.SetHandleAsInvalid();
}
}
internal Host(Interop.WasmConfigHandle config)
{
var engine = Interop.wasm_engine_new_with_config(config);
config.SetHandleAsInvalid();
Initialize(engine);
}
private void Initialize(Interop.EngineHandle engine)
{
if (engine.IsInvalid)
{
throw new WasmtimeException("Failed to create Wasmtime engine.");
}
var store = Interop.wasm_store_new(engine);
if (store.IsInvalid)
{
throw new WasmtimeException("Failed to create Wasmtime store.");
}
var linker = Interop.wasmtime_linker_new(store);
if (linker.IsInvalid)
{
throw new WasmtimeException("Failed to create Wasmtime linker.");
}
Interop.wasmtime_linker_allow_shadowing(linker, allowShadowing: true);
Engine = engine;
Store = store;
Linker = linker;
}
private void CheckDisposed()
{
if (Engine.IsInvalid)
{
throw new ObjectDisposedException(typeof(Host).FullName);
}
}
private Function DefineFunction(string moduleName, string name, Delegate func, bool hasReturn)
{
if (moduleName is null)
{
throw new ArgumentNullException(nameof(moduleName));
}
if (name is null)
{
throw new ArgumentNullException(nameof(name));
}
if (func is null)
{
throw new ArgumentNullException(nameof(func));
}
var function = new Function(Store, func, hasReturn);
if (!Define(moduleName, name, Interop.wasm_func_as_extern(function.Handle)))
{
function.Dispose();
throw new WasmtimeException($"Failed to define function '{name}' in module '{moduleName}'.");
}
_callbacks.Add(function.Callback);
return function;
}
private bool Define(string moduleName, string name, IntPtr ext)
{
var moduleNameBytes = Encoding.UTF8.GetBytes(moduleName);
var nameBytes = Encoding.UTF8.GetBytes(name);
unsafe
{
fixed (byte* moduleNamePtr = moduleNameBytes)
fixed (byte* namePtr = nameBytes)
{
Interop.wasm_byte_vec_t moduleNameVec = new Interop.wasm_byte_vec_t();
moduleNameVec.size = (UIntPtr)moduleNameBytes.Length;
moduleNameVec.data = moduleNamePtr;
Interop.wasm_byte_vec_t nameVec = new Interop.wasm_byte_vec_t();
nameVec.size = (UIntPtr)nameBytes.Length;
nameVec.data = namePtr;
var error = Interop.wasmtime_linker_define(Linker, ref moduleNameVec, ref nameVec, ext);
if (error == IntPtr.Zero)
return true;
Interop.wasmtime_error_delete(error);
return false;
}
}
}
internal Interop.EngineHandle Engine { get; private set; }
internal Interop.StoreHandle Store { get; private set; }
internal Interop.LinkerHandle Linker { get; private set; }
private List<Delegate> _callbacks = new List<Delegate>();
}
}

View File

@@ -1,247 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Wasmtime
{
/// <summary>
/// Represents the Wasmtime compiler strategy.
/// </summary>
public enum CompilerStrategy
{
/// <summary>
/// Automatically pick the compiler strategy.
/// </summary>
Auto,
/// <summary>
/// Use the Cranelift compiler.
/// </summary>
Cranelift,
/// <summary>
/// Use the Lightbeam compiler.
/// </summary>
Lightbeam
}
/// <summary>
/// Represents the Wasmtime optimization level.
/// </summary>
public enum OptimizationLevel
{
/// <summary>
/// Disable optimizations.
/// </summary>
None,
/// <summary>
/// Optimize for speed.
/// </summary>
Speed,
/// <summary>
/// Optimize for speed and size.
/// </summary>
SpeedAndSize
}
/// <summary>
/// Represents a builder of <see cref="Host"/> instances.
/// </summary>
public class HostBuilder
{
/// <summary>
/// Sets whether or not to enable debug information.
/// </summary>
/// <param name="enable">True to enable debug information or false to disable.</param>
/// <returns>Returns the current builder.</returns>
public HostBuilder WithDebugInfo(bool enable)
{
_enableDebugInfo = enable;
return this;
}
/// <summary>
/// Sets whether or not enable WebAssembly threads support.
/// </summary>
/// <param name="enable">True to enable WebAssembly threads support or false to disable.</param>
/// <returns>Returns the current builder.</returns>
public HostBuilder WithWasmThreads(bool enable)
{
_enableWasmThreads = enable;
return this;
}
/// <summary>
/// Sets whether or not enable WebAssembly reference types support.
/// </summary>
/// <param name="enable">True to enable WebAssembly reference types support or false to disable.</param>
/// <returns>Returns the current builder.</returns>
public HostBuilder WithReferenceTypes(bool enable)
{
_enableReferenceTypes = enable;
return this;
}
/// <summary>
/// Sets whether or not enable WebAssembly SIMD support.
/// </summary>
/// <param name="enable">True to enable WebAssembly SIMD support or false to disable.</param>
/// <returns>Returns the current builder.</returns>
public HostBuilder WithSIMD(bool enable)
{
_enableSIMD = enable;
return this;
}
/// <summary>
/// Sets whether or not enable WebAssembly multi-value support.
/// </summary>
/// <param name="enable">True to enable WebAssembly multi-value support or false to disable.</param>
/// <returns>Returns the current builder.</returns>
public HostBuilder WithMultiValue(bool enable)
{
_enableMultiValue = enable;
return this;
}
/// <summary>
/// Sets whether or not enable WebAssembly bulk memory support.
/// </summary>
/// <param name="enable">True to enable WebAssembly bulk memory support or false to disable.</param>
/// <returns>Returns the current builder.</returns>
public HostBuilder WithBulkMemory(bool enable)
{
_enableBulkMemory = enable;
return this;
}
/// <summary>
/// Sets the compiler strategy to use.
/// </summary>
/// <param name="strategy">The compiler strategy to use.</param>
/// <returns>Returns the current builder.</returns>
public HostBuilder WithCompilerStrategy(CompilerStrategy strategy)
{
switch (strategy)
{
case CompilerStrategy.Auto:
_strategy = Interop.wasmtime_strategy_t.WASMTIME_STRATEGY_AUTO;
break;
case CompilerStrategy.Cranelift:
_strategy = Interop.wasmtime_strategy_t.WASMTIME_STRATEGY_CRANELIFT;
break;
case CompilerStrategy.Lightbeam:
_strategy = Interop.wasmtime_strategy_t.WASMTIME_STRATEGY_LIGHTBEAM;
break;
default:
throw new ArgumentOutOfRangeException(nameof(strategy));
}
return this;
}
/// <summary>
/// Sets whether or not enable the Cranelift debug verifier.
/// </summary>
/// <param name="enable">True to enable the Cranelift debug verifier or false to disable.</param>
/// <returns>Returns the current builder.</returns>
public HostBuilder WithCraneliftDebugVerifier(bool enable)
{
_enableCraneliftDebugVerifier = enable;
return this;
}
/// <summary>
/// Sets the optimization level to use.
/// </summary>
/// <param name="level">The optimization level to use.</param>
/// <returns>Returns the current builder.</returns>
public HostBuilder WithOptimizationLevel(OptimizationLevel level)
{
switch (level)
{
case OptimizationLevel.None:
_optLevel = Interop.wasmtime_opt_level_t.WASMTIME_OPT_LEVEL_NONE;
break;
case OptimizationLevel.Speed:
_optLevel = Interop.wasmtime_opt_level_t.WASMTIME_OPT_LEVEL_SPEED;
break;
case OptimizationLevel.SpeedAndSize:
_optLevel = Interop.wasmtime_opt_level_t.WASMTIME_OPT_LEVEL_SPEED_AND_SIZE;
break;
default:
throw new ArgumentOutOfRangeException(nameof(level));
}
return this;
}
/// <summary>
/// Builds the <see cref="Host" /> instance.
/// </summary>
/// <returns>Returns the new <see cref="Host" /> instance.</returns>
public Host Build()
{
var config = Interop.wasm_config_new();
if (_enableDebugInfo.HasValue)
{
Interop.wasmtime_config_debug_info_set(config, _enableDebugInfo.Value);
}
if (_enableWasmThreads.HasValue)
{
Interop.wasmtime_config_wasm_threads_set(config, _enableWasmThreads.Value);
}
if (_enableReferenceTypes.HasValue)
{
Interop.wasmtime_config_wasm_reference_types_set(config, _enableReferenceTypes.Value);
}
if (_enableSIMD.HasValue)
{
Interop.wasmtime_config_wasm_simd_set(config, _enableSIMD.Value);
}
if (_enableBulkMemory.HasValue)
{
Interop.wasmtime_config_wasm_bulk_memory_set(config, _enableBulkMemory.Value);
}
if (_enableMultiValue.HasValue)
{
Interop.wasmtime_config_wasm_multi_value_set(config, _enableMultiValue.Value);
}
if (_strategy.HasValue)
{
Interop.wasmtime_config_strategy_set(config, _strategy.Value);
}
if (_enableCraneliftDebugVerifier.HasValue)
{
Interop.wasmtime_config_cranelift_debug_verifier_set(config, _enableCraneliftDebugVerifier.Value);
}
if (_optLevel.HasValue)
{
Interop.wasmtime_config_cranelift_opt_level_set(config, _optLevel.Value);
}
return new Host(config);
}
private bool? _enableDebugInfo;
private bool? _enableWasmThreads;
private bool? _enableReferenceTypes;
private bool? _enableSIMD;
private bool? _enableBulkMemory;
private bool? _enableMultiValue;
private Interop.wasmtime_strategy_t? _strategy;
private bool? _enableCraneliftDebugVerifier;
private Interop.wasmtime_opt_level_t? _optLevel;
}
}

View File

@@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace Wasmtime.Imports
{
/// <summary>
/// Represents a function imported to a WebAssembly module.
/// </summary>
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));
}
}
/// <summary>
/// The parameters of the imported function.
/// </summary>
public IReadOnlyList<ValueKind> Parameters { get; private set; }
/// <summary>
/// The results of the imported function.
/// </summary>
public IReadOnlyList<ValueKind> Results { get; private set; }
}
}

View File

@@ -1,30 +0,0 @@
using System;
using System.Diagnostics;
namespace Wasmtime.Imports
{
/// <summary>
/// Represents a global variable imported to a WebAssembly module.
/// </summary>
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;
}
/// <summary>
/// The kind of value for the global variable.
/// </summary>
public ValueKind Kind { get; private set; }
/// <summary>
/// Determines whether or not the global variable is mutable.
/// </summary>
public bool IsMutable { get; private set; }
}
}

View File

@@ -1,43 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace Wasmtime.Imports
{
/// <summary>
/// The base class for import types.
/// </summary>
public abstract class Import
{
internal Import(IntPtr importType)
{
unsafe
{
Handle = importType;
var moduleName = Interop.wasm_importtype_module(Handle);
ModuleName = Marshal.PtrToStringUTF8((IntPtr)moduleName->data, (int)moduleName->size);
var name = Interop.wasm_importtype_name(Handle);
Name = Marshal.PtrToStringUTF8((IntPtr)name->data, (int)name->size);
}
}
/// <summary>
/// The module name of the import.
/// </summary>
public string ModuleName { get; private set; }
/// <summary>
/// The name of the import.
/// </summary>
public string Name { get; private set; }
internal IntPtr Handle { get; private set; }
/// <inheritdoc/>
public override string ToString()
{
return $"{ModuleName}{(string.IsNullOrEmpty(ModuleName) ? "" : ".")}{Name}";
}
}
}

View File

@@ -1,102 +0,0 @@
using System;
using System.Collections.Generic;
namespace Wasmtime.Imports
{
/// <summary>
/// Represents imported functions, globals, tables, and memories to a WebAssembly module.
/// </summary>
public class Imports : IDisposable
{
internal Imports(Module module)
{
Interop.wasm_importtype_vec_t imports;
Interop.wasm_module_imports(module.Handle, out imports);
var all = new List<Import>((int)imports.size);
var functions = new List<FunctionImport>();
var globals = new List<GlobalImport>();
var tables = new List<TableImport>();
var memories = new List<MemoryImport>();
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;
}
/// <inheritdoc/>
public unsafe void Dispose()
{
if (!(_imports.data is null))
{
Interop.wasm_importtype_vec_delete(ref _imports);
_imports.data = null;
}
}
/// <summary>
/// The imported functions required by a WebAssembly module.
/// </summary>
public IReadOnlyList<FunctionImport> Functions { get; private set; }
/// <summary>
/// The imported globals required by a WebAssembly module.
/// </summary>
public IReadOnlyList<GlobalImport> Globals { get; private set; }
/// <summary>
/// The imported tables required by a WebAssembly module.
/// </summary>
public IReadOnlyList<TableImport> Tables { get; private set; }
/// <summary>
/// The imported memories required by a WebAssembly module.
/// </summary>
public IReadOnlyList<MemoryImport> Memories { get; private set; }
internal IReadOnlyList<Import> All { get; private set; }
private Interop.wasm_importtype_vec_t _imports;
}
}

View File

@@ -1,35 +0,0 @@
using System;
using System.Diagnostics;
namespace Wasmtime.Imports
{
/// <summary>
/// Represents a memory imported to a WebAssembly module.
/// </summary>
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;
}
}
/// <summary>
/// The minimum memory size (in WebAssembly page units).
/// </summary>
public uint Minimum { get; private set; }
/// <summary>
/// The maximum memory size (in WebAssembly page units).
/// </summary>
public uint Maximum { get; private set; }
}
}

View File

@@ -1,42 +0,0 @@
using System;
using System.Diagnostics;
namespace Wasmtime.Imports
{
/// <summary>
/// Represents a table imported to a WebAssembly module.
/// </summary>
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;
}
}
/// <summary>
/// The value kind of the table.
/// </summary>
public ValueKind Kind { get; private set; }
/// <summary>
/// The minimum number of elements in the table.
/// </summary>
public uint Minimum { get; private set; }
/// <summary>
/// The maximum number of elements in the table.
/// </summary>
public uint Maximum { get; private set; }
}
}

View File

@@ -1,115 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Dynamic;
using Wasmtime.Externs;
namespace Wasmtime
{
/// <summary>
/// Represents an instantiated WebAssembly module.
/// </summary>
public class Instance : DynamicObject, IDisposable
{
/// <summary>
/// The WebAssembly module associated with the instantiation.
/// </summary>
public Module Module { get; private set; }
/// <summary>
/// The external (instantiated) collection of functions, globals, tables, and memories.
/// </summary>
public Wasmtime.Externs.Externs Externs { get; private set; }
/// <inheritdoc/>
public unsafe void Dispose()
{
if (!Handle.IsInvalid)
{
Handle.Dispose();
Handle.SetHandleAsInvalid();
}
if (!(_externs.data is null))
{
Interop.wasm_extern_vec_delete(ref _externs);
_externs.data = null;
}
}
/// <inheritdoc/>
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;
}
/// <inheritdoc/>
public override bool TrySetMember(SetMemberBinder binder, object value)
{
if (_globals.TryGetValue(binder.Name, out var global))
{
global.Value = value;
return true;
}
return false;
}
/// <inheritdoc/>
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;
}
internal Instance(Interop.LinkerHandle linker, Module module)
{
Module = module;
unsafe
{
var error = Interop.wasmtime_linker_instantiate(linker, module.Handle, out var handle, out var trap);
Handle = handle;
if (error != IntPtr.Zero)
{
throw WasmtimeException.FromOwnedError(error);
}
if (trap != IntPtr.Zero)
{
throw TrapException.FromOwnedTrap(trap);
}
}
if (Handle.IsInvalid)
{
throw new WasmtimeException("Failed to create Wasmtime instance.");
}
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);
}
internal Interop.InstanceHandle Handle { get; private set; }
private Interop.wasm_extern_vec_t _externs;
private Dictionary<string, ExternFunction> _functions;
private Dictionary<string, ExternGlobal> _globals;
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,72 +0,0 @@
using System;
using System.Text;
using System.Buffers.Binary;
namespace Wasmtime
{
/// <summary>
/// Represents a WebAssembly memory.
/// </summary>
public class Memory : MemoryBase, IDisposable
{
/// <summary>
/// The size, in bytes, of a WebAssembly memory page.
/// </summary>
public const int PageSize = 65536;
/// <summary>
/// The minimum memory size (in WebAssembly page units).
/// </summary>
public uint Minimum { get; private set; }
/// <summary>
/// The maximum memory size (in WebAssembly page units).
/// </summary>
public uint Maximum { get; private set; }
/// <inheritdoc/>
public void Dispose()
{
if (!Handle.IsInvalid)
{
Handle.Dispose();
Handle.SetHandleAsInvalid();
}
}
internal Memory(Interop.StoreHandle store, 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;
unsafe
{
Interop.wasm_limits_t limits = new Interop.wasm_limits_t();
limits.min = minimum;
limits.max = maximum;
using var memoryType = Interop.wasm_memorytype_new(&limits);
Handle = Interop.wasm_memory_new(store, memoryType);
if (Handle.IsInvalid)
{
throw new WasmtimeException("Failed to create Wasmtime memory.");
}
}
}
protected override IntPtr MemoryHandle => Handle.DangerousGetHandle();
internal Interop.MemoryHandle Handle { get; private set; }
}
}

View File

@@ -1,236 +0,0 @@
using System;
using System.Text;
using System.Buffers.Binary;
namespace Wasmtime
{
public abstract class MemoryBase
{
/// <summary>
/// The span of the memory.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public unsafe Span<byte> Span
{
get
{
var data = Interop.wasm_memory_data(MemoryHandle);
var size = Convert.ToInt32(Interop.wasm_memory_data_size(MemoryHandle).ToUInt32());
return new Span<byte>(data, size);
}
}
/// <summary>
/// Reads a UTF-8 string from memory.
/// </summary>
/// <param name="address">The zero-based address to read from.</param>
/// <param name="length">The length of bytes to read.</param>
/// <returns>Returns the string read from memory.</returns>
public string ReadString(int address, int length)
{
return Encoding.UTF8.GetString(Span.Slice(address, length));
}
/// <summary>
/// Reads a null-terminated UTF-8 string from memory.
/// </summary>
/// <param name="address">The zero-based address to read from.</param>
/// <returns>Returns the string read from memory.</returns>
public string ReadNullTerminatedString(int address)
{
var slice = Span.Slice(address);
var terminator = slice.IndexOf((byte)0);
if (terminator == -1)
{
throw new InvalidOperationException("string is not null terminated");
}
return Encoding.UTF8.GetString(slice.Slice(0, terminator));
}
/// <summary>
/// Writes a UTF-8 string at the given address.
/// </summary>
/// <param name="address">The zero-based address to write to.</param>
/// <param name="value">The string to write.</param>
/// <return>Returns the number of bytes written.</return>
public int WriteString(int address, string value)
{
return Encoding.UTF8.GetBytes(value, Span.Slice(address));
}
/// <summary>
/// Reads a byte from memory.
/// </summary>
/// <param name="address">The zero-based address to read from.</param>
/// <returns>Returns the byte read from memory.</returns>
public byte ReadByte(int address)
{
return Span[address];
}
/// <summary>
/// Writes a byte to memory.
/// </summary>
/// <param name="address">The zero-based address to write to.</param>
/// <param name="value">The byte to write.</param>
public void WriteByte(int address, byte value)
{
Span[address] = value;
}
/// <summary>
/// Reads a short from memory.
/// </summary>
/// <param name="address">The zero-based address to read from.</param>
/// <returns>Returns the short read from memory.</returns>
public short ReadInt16(int address)
{
return BinaryPrimitives.ReadInt16LittleEndian(Span.Slice(address, 2));
}
/// <summary>
/// Writes a short to memory.
/// </summary>
/// <param name="address">The zero-based address to write to.</param>
/// <param name="value">The short to write.</param>
public void WriteInt16(int address, short value)
{
BinaryPrimitives.WriteInt16LittleEndian(Span.Slice(address, 2), value);
}
/// <summary>
/// Reads an int from memory.
/// </summary>
/// <param name="address">The zero-based address to read from.</param>
/// <returns>Returns the int read from memory.</returns>
public int ReadInt32(int address)
{
return BinaryPrimitives.ReadInt32LittleEndian(Span.Slice(address, 4));
}
/// <summary>
/// Writes an int to memory.
/// </summary>
/// <param name="address">The zero-based address to write to.</param>
/// <param name="value">The int to write.</param>
public void WriteInt32(int address, int value)
{
BinaryPrimitives.WriteInt32LittleEndian(Span.Slice(address, 4), value);
}
/// <summary>
/// Reads a long from memory.
/// </summary>
/// <param name="address">The zero-based address to read from.</param>
/// <returns>Returns the long read from memory.</returns>
public long ReadInt64(int address)
{
return BinaryPrimitives.ReadInt64LittleEndian(Span.Slice(address, 8));
}
/// <summary>
/// Writes a long to memory.
/// </summary>
/// <param name="address">The zero-based address to write to.</param>
/// <param name="value">The long to write.</param>
public void WriteInt64(int address, long value)
{
BinaryPrimitives.WriteInt64LittleEndian(Span.Slice(address, 8), value);
}
/// <summary>
/// Reads an IntPtr from memory.
/// </summary>
/// <param name="address">The zero-based address to read from.</param>
/// <returns>Returns the IntPtr read from memory.</returns>
public IntPtr ReadIntPtr(int address)
{
if (IntPtr.Size == 4)
{
return (IntPtr)ReadInt32(address);
}
return (IntPtr)ReadInt64(address);
}
/// <summary>
/// Writes an IntPtr to memory.
/// </summary>
/// <param name="address">The zero-based address to write to.</param>
/// <param name="value">The IntPtr to write.</param>
public void WriteIntPtr(int address, IntPtr value)
{
if (IntPtr.Size == 4)
{
WriteInt32(address, value.ToInt32());
}
else
{
WriteInt64(address, value.ToInt64());
}
}
/// <summary>
/// Reads a long from memory.
/// </summary>
/// <param name="address">The zero-based address to read from.</param>
/// <returns>Returns the long read from memory.</returns>
public float ReadSingle(int address)
{
unsafe
{
var i = ReadInt32(address);
return *((float*)&i);
}
}
/// <summary>
/// Writes a single to memory.
/// </summary>
/// <param name="address">The zero-based address to write to.</param>
/// <param name="value">The single to write.</param>
public void WriteSingle(int address, float value)
{
unsafe
{
WriteInt32(address, *(int*)&value);
}
}
/// <summary>
/// Reads a double from memory.
/// </summary>
/// <param name="address">The zero-based address to read from.</param>
/// <returns>Returns the double read from memory.</returns>
public double ReadDouble(int address)
{
unsafe
{
var i = ReadInt64(address);
return *((double*)&i);
}
}
/// <summary>
/// Writes a double to memory.
/// </summary>
/// <param name="address">The zero-based address to write to.</param>
/// <param name="value">The double to write.</param>
public void WriteDouble(int address, double value)
{
unsafe
{
WriteInt64(address, *(long*)&value);
}
}
protected abstract IntPtr MemoryHandle { get; }
}
}

View File

@@ -1,68 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace Wasmtime
{
/// <summary>
/// Represents a WebAssembly module.
/// </summary>
public class Module : IDisposable
{
/// <summary>
/// The name of the module.
/// </summary>
public string Name { get; private set; }
/// <summary>
/// The imports of the module.
/// </summary>
public Wasmtime.Imports.Imports Imports { get; private set; }
/// <summary>
/// The exports of the module.
/// </summary>
/// <value></value>
public Wasmtime.Exports.Exports Exports { get; private set; }
/// <inheritdoc/>
public void Dispose()
{
if (!Handle.IsInvalid)
{
Handle.Dispose();
Handle.SetHandleAsInvalid();
}
if (!(Imports is null))
{
Imports.Dispose();
Imports = null;
}
}
internal Module(Interop.StoreHandle store, string name, byte[] bytes)
{
unsafe
{
fixed (byte *ptr = bytes)
{
Interop.wasm_byte_vec_t vec;
vec.size = (UIntPtr)bytes.Length;
vec.data = ptr;
Handle = Interop.wasm_module_new(store, ref vec);
}
if (Handle.IsInvalid)
{
throw new WasmtimeException($"WebAssembly module '{name}' is not valid.");
}
}
Name = name;
Imports = new Wasmtime.Imports.Imports(this);
Exports = new Wasmtime.Exports.Exports(this);
}
internal Interop.ModuleHandle Handle { get; private set; }
}
}

View File

@@ -1,94 +0,0 @@
using System;
namespace Wasmtime
{
/// <summary>
/// Represents a mutable WebAssembly global value.
/// </summary>
public class MutableGlobal<T> : IDisposable
{
/// <summary>
/// The value of the global.
/// </summary>
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);
return (T)Interop.ToObject(v);
}
}
set
{
if (Handle is null)
{
throw new InvalidOperationException("The global cannot be used before it is instantiated.");
}
var v = Interop.ToValue(value, Kind);
unsafe
{
Interop.wasm_global_set(Handle.DangerousGetHandle(), &v);
}
}
}
/// <summary>
/// Gets the value kind of the global.
/// </summary>
/// <value></value>
public ValueKind Kind { get; private set; }
/// <inheritdoc/>
public void Dispose()
{
if (!Handle.IsInvalid)
{
Handle.Dispose();
Handle.SetHandleAsInvalid();
}
}
internal MutableGlobal(Interop.StoreHandle store, T initialValue)
{
if (!Interop.TryGetValueKind(typeof(T), out var kind))
{
throw new WasmtimeException($"Mutable global variables cannot be of type '{typeof(T).ToString()}'.");
}
Kind = kind;
var value = Interop.ToValue((object)initialValue, Kind);
var valueType = Interop.wasm_valtype_new(value.kind);
var valueTypeHandle = valueType.DangerousGetHandle();
valueType.SetHandleAsInvalid();
using var globalType = Interop.wasm_globaltype_new(
valueTypeHandle,
Interop.wasm_mutability_t.WASM_VAR
);
unsafe
{
Handle = Interop.wasm_global_new(store, globalType, &value);
if (Handle.IsInvalid)
{
throw new WasmtimeException("Failed to create mutable Wasmtime global.");
}
}
}
internal Interop.GlobalHandle Handle { get; set; }
}
}

View File

@@ -1,49 +0,0 @@
using System;
using System.Runtime.Serialization;
using System.Text;
namespace Wasmtime
{
/// <summary>
/// The exception for WebAssembly traps.
/// </summary>
[Serializable]
public class TrapException : WasmtimeException
{
/// <inheritdoc/>
public TrapException() { }
/// <inheritdoc/>
public TrapException(string message) : base(message) { }
/// <inheritdoc/>
public TrapException(string message, Exception inner) : base(message, inner) { }
/// <inheritdoc/>
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 byteSpan = new ReadOnlySpan<byte>(bytes.data, checked((int)bytes.size));
int indexOfNull = byteSpan.LastIndexOf((byte)0);
if (indexOfNull != -1)
{
byteSpan = byteSpan.Slice(0, indexOfNull);
}
var message = Encoding.UTF8.GetString(byteSpan);
Interop.wasm_byte_vec_delete(ref bytes);
Interop.wasm_trap_delete(trap);
return new TrapException(message);
}
}
// TODO: expose trap frames
}
}

View File

@@ -1,35 +0,0 @@
using System;
namespace Wasmtime
{
/// <summary>
/// Represents the possible kinds of WebAssembly values.
/// </summary>
public enum ValueKind : byte
{
/// <summary>
/// The value is a 32-bit integer.
/// </summary>
Int32,
/// <summary>
/// The value is a 64-bit integer.
/// </summary>
Int64,
/// <summary>
/// The value is a 32-bit floating point number.
/// </summary>
Float32,
/// <summary>
/// The value is a 64-bit floating point number.
/// </summary>
Float64,
/// <summary>
/// The value is a reference.
/// </summary>
AnyRef = 128,
/// <summary>
/// The value is a function reference.
/// </summary>
FuncRef,
}
}

View File

@@ -1,411 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Wasmtime
{
/// <summary>
/// Represents a WASI configuration.
/// </summary>
public class WasiConfiguration
{
/// <summary>
/// Adds a command line argument to the configuration.
/// </summary>
/// <param name="arg">The command line argument to add.</param>
/// <returns>Returns the current configuration.</returns>
public WasiConfiguration WithArg(string arg)
{
if (arg is null)
{
throw new ArgumentNullException(nameof(arg));
}
if (_inheritArgs)
{
_args.Clear();
_inheritArgs = false;
}
_args.Add(arg);
return this;
}
/// <summary>
/// Adds multiple command line arguments to the configuration.
/// </summary>
/// <param name="args">The command line arguments to add.</param>
/// <returns>Returns the current configuration.</returns>
public WasiConfiguration WithArgs(IEnumerable<string> args)
{
if (args is null)
{
throw new ArgumentNullException(nameof(args));
}
if (_inheritArgs)
{
_args.Clear();
_inheritArgs = false;
}
foreach (var arg in args)
{
_args.Add(arg);
}
return this;
}
/// <summary>
/// Adds multiple command line arguments to the configuration.
/// </summary>
/// <param name="args">The command line arguments to add.</param>
/// <returns>Returns the current configuration.</returns>
public WasiConfiguration WithArgs(params string[] args)
{
return WithArgs((IEnumerable<string>)args);
}
/// <summary>
/// Sets the configuration to inherit command line arguments.
/// </summary>
/// <remarks>Any explicitly specified command line arguments will be removed.</remarks>
/// <returns>Returns the current configuration.</returns>
public WasiConfiguration WithInheritedArgs()
{
_inheritArgs = true;
_args.Clear();
_args.AddRange(Environment.GetCommandLineArgs());
return this;
}
/// <summary>
/// Adds an environment variable to the configuration.
/// </summary>
/// <param name="name">The name of the environment variable.</param>
/// <param name="value">The value of the environment variable.</param>
/// <returns>Returns the current configuration.</returns>
public WasiConfiguration WithEnvironmentVariable(string name, string value)
{
if (name is null)
{
throw new ArgumentNullException(nameof(name));
}
if (value is null)
{
throw new ArgumentNullException(nameof(value));
}
if (string.IsNullOrEmpty(name))
{
throw new ArgumentException("Environment variable name cannot be empty.", nameof(name));
}
_inheritEnv = false;
_vars.Add((name, value));
return this;
}
/// <summary>
/// Adds multiple environment variables to the configuration.
/// </summary>
/// <param name="vars">The name-value tuples of the environment variables to add.</param>
/// <returns>Returns the current configuration.</returns>
public WasiConfiguration WithEnvironmentVariables(IEnumerable<(string,string)> vars)
{
if (vars is null)
{
throw new ArgumentNullException(nameof(vars));
}
_inheritEnv = false;
foreach (var v in vars)
{
_vars.Add(v);
}
return this;
}
/// <summary>
/// Sets the configuration to inherit environment variables.
/// </summary>
/// <remarks>Any explicitly specified environment variables will be removed.</remarks>
/// <returns>Returns the current configuration.</returns>
public WasiConfiguration WithInheritedEnvironment()
{
_inheritEnv = true;
_vars.Clear();
return this;
}
/// <summary>
/// Sets the configuration to use the given file path as stdin.
/// </summary>
/// <param name="path">The file to use as stdin.</param>
/// <returns>Returns the current configuration.</returns>
public WasiConfiguration WithStandardInput(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException("The path cannot be null or empty.", nameof(path));
}
_inheritStandardInput = false;
_standardInputPath = path;
return this;
}
/// <summary>
/// Sets the configuration to inherit stdin.
/// </summary>
/// <remarks>Any explicitly specified stdin file will be removed.</remarks>
/// <returns>Returns the current configuration.</returns>
public WasiConfiguration WithInheritedStandardInput()
{
_inheritStandardInput = true;
_standardInputPath = null;
return this;
}
/// <summary>
/// Sets the configuration to use the given file path as stdout.
/// </summary>
/// <param name="path">The file to use as stdout.</param>
/// <returns>Returns the current configuration.</returns>
public WasiConfiguration WithStandardOutput(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException("The path cannot be null or empty.", nameof(path));
}
_inheritStandardOutput = false;
_standardOutputPath = path;
return this;
}
/// <summary>
/// Sets the configuration to inherit stdout.
/// </summary>
/// <remarks>Any explicitly specified stdout file will be removed.</remarks>
/// <returns>Returns the current configuration.</returns>
public WasiConfiguration WithInheritedStandardOutput()
{
_inheritStandardOutput = true;
_standardOutputPath = null;
return this;
}
/// <summary>
/// Sets the configuration to use the given file path as stderr.
/// </summary>
/// <param name="path">The file to use as stderr.</param>
/// <returns>Returns the current configuration.</returns>
public WasiConfiguration WithStandardError(string path)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException("The path cannot be null or empty.", nameof(path));
}
_inheritStandardError = false;
_standardErrorPath = path;
return this;
}
/// <summary>
/// Sets the configuration to inherit stderr.
/// </summary>
/// <remarks>Any explicitly specified stderr file will be removed.</remarks>
/// <returns>Returns the current configuration.</returns>
public WasiConfiguration WithInheritedStandardError()
{
_inheritStandardError = true;
_standardErrorPath = null;
return this;
}
/// <summary>
/// Adds a preopen directory to the configuration.
/// </summary>
/// <param name="path">The path to the directory to add.</param>
/// <param name="guestPath">The path the guest will use to open the directory.</param>
/// <returns>Returns the current configuration.</returns>
public WasiConfiguration WithPreopenedDirectory(string path, string guestPath)
{
if (string.IsNullOrEmpty(path))
{
throw new ArgumentException("The path cannot be null or empty.", nameof(path));
}
if (string.IsNullOrEmpty(guestPath))
{
throw new ArgumentException("The guest path cannot be null or empty.", nameof(guestPath));
}
_preopenDirs.Add((path, guestPath));
return this;
}
internal Interop.WasiInstanceHandle CreateWasi(Interop.StoreHandle store, string name)
{
var config = Interop.wasi_config_new();
SetConfigArgs(config);
SetEnvironmentVariables(config);
SetStandardIn(config);
SetStandardOut(config);
SetStandardError(config);
SetPreopenDirectories(config);
IntPtr trap;
var wasi = Interop.wasi_instance_new(store, name, config, out trap);
config.SetHandleAsInvalid();
if (trap != IntPtr.Zero)
{
throw TrapException.FromOwnedTrap(trap);
}
if (wasi.IsInvalid)
{
throw new WasmtimeException($"Failed to create instance for WASI module '{name}'.");
}
return wasi;
}
private unsafe void SetConfigArgs(Interop.WasiConfigHandle config)
{
// Don't call wasi_config_inherit_argv as the command line to the .NET program may not be
// the same as the process' command line (e.g. `dotnet foo.dll foo bar baz` => "foo.dll foo bar baz").
if (_args.Count == 0)
{
return;
}
var (args, handles) = Interop.ToUTF8PtrArray(_args);
try
{
Interop.wasi_config_set_argv(config, _args.Count, args);
}
finally
{
foreach (var handle in handles)
{
handle.Free();
}
}
}
private unsafe void SetEnvironmentVariables(Interop.WasiConfigHandle config)
{
if (_inheritEnv)
{
Interop.wasi_config_inherit_env(config);
return;
}
if (_vars.Count == 0)
{
return;
}
var (names, nameHandles) = Interop.ToUTF8PtrArray(_vars.Select(var => var.Name).ToArray());
var (values, valueHandles) = Interop.ToUTF8PtrArray(_vars.Select(var => var.Value).ToArray());
try
{
Interop.wasi_config_set_env(config, _vars.Count, names, values);
}
finally
{
foreach (var handle in nameHandles)
{
handle.Free();
}
foreach (var handle in valueHandles)
{
handle.Free();
}
}
}
private void SetStandardIn(Interop.WasiConfigHandle config)
{
if (_inheritStandardInput)
{
Interop.wasi_config_inherit_stdin(config);
return;
}
if (!string.IsNullOrEmpty(_standardInputPath))
{
if (!Interop.wasi_config_set_stdin_file(config, _standardInputPath))
{
throw new InvalidOperationException($"Failed to set stdin to file '{_standardInputPath}'.");
}
}
}
private void SetStandardOut(Interop.WasiConfigHandle config)
{
if (_inheritStandardOutput)
{
Interop.wasi_config_inherit_stdout(config);
return;
}
if (!string.IsNullOrEmpty(_standardOutputPath))
{
if (!Interop.wasi_config_set_stdout_file(config, _standardOutputPath))
{
throw new InvalidOperationException($"Failed to set stdout to file '{_standardOutputPath}'.");
}
}
}
private void SetStandardError(Interop.WasiConfigHandle config)
{
if (_inheritStandardError)
{
Interop.wasi_config_inherit_stderr(config);
return;
}
if (!string.IsNullOrEmpty(_standardErrorPath))
{
if (!Interop.wasi_config_set_stderr_file(config, _standardErrorPath))
{
throw new InvalidOperationException($"Failed to set stderr to file '{_standardErrorPath}'.");
}
}
}
private void SetPreopenDirectories(Interop.WasiConfigHandle config)
{
foreach (var dir in _preopenDirs)
{
if (!Interop.wasi_config_preopen_dir(config, dir.Path, dir.GuestPath))
{
throw new InvalidOperationException($"Failed to preopen directory '{dir.Path}'.");
}
}
}
private readonly List<string> _args = new List<string>();
private readonly List<(string Name, string Value)> _vars = new List<(string, string)>();
private string _standardInputPath;
private string _standardOutputPath;
private string _standardErrorPath;
private readonly List<(string Path, string GuestPath)> _preopenDirs = new List<(string, string)>();
private bool _inheritArgs = false;
private bool _inheritEnv = false;
private bool _inheritStandardInput = false;
private bool _inheritStandardOutput = false;
private bool _inheritStandardError = false;
}
}

View File

@@ -1,77 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.6.0" />
</ItemGroup>
<PropertyGroup>
<AssemblyName>Wasmtime.Dotnet</AssemblyName>
<PackageId>Wasmtime</PackageId>
<Version>$(WasmtimeVersion)-preview1</Version>
<Authors>Peter Huene</Authors>
<Owners>Peter Huene</Owners>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<RepositoryUrl>https://github.com/bytecodealliance/wasmtime</RepositoryUrl>
<PackageReleaseNotes>Initial release of Wasmtime for .NET.</PackageReleaseNotes>
<Summary>A .NET API for Wasmtime, a standalone WebAssembly runtime</Summary>
<PackageTags>webassembly, .net, wasm, wasmtime</PackageTags>
<Title>Wasmtime</Title>
<PackageDescription>
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.
</PackageDescription>
</PropertyGroup>
<Target Name="PackWasmtime" BeforeTargets="_GetPackageFiles">
<PropertyGroup>
<WasmtimeArchitecture>x86_64</WasmtimeArchitecture>
<ReleaseURLBase>https://github.com/bytecodealliance/wasmtime/releases/download/v$(WasmtimeVersion)/wasmtime-v$(WasmtimeVersion)-$(WasmtimeArchitecture)</ReleaseURLBase>
</PropertyGroup>
<ItemGroup>
<WasmtimeDownload Include="Linux">
<URL>$(ReleaseURLBase)-linux-c-api.tar.xz</URL>
<DownloadFolder>$(IntermediateOutputPath)wasmtime-linux</DownloadFolder>
<DownloadFilename>linux.tar.xz</DownloadFilename>
<WasmtimeLibraryFilename>libwasmtime.so</WasmtimeLibraryFilename>
<PackagePath>runtimes/linux-x64/native</PackagePath>
</WasmtimeDownload>
<WasmtimeDownload Include="macOS">
<URL>$(ReleaseURLBase)-macos-c-api.tar.xz</URL>
<DownloadFolder>$(IntermediateOutputPath)wasmtime-macos</DownloadFolder>
<DownloadFilename>macos.tar.xz</DownloadFilename>
<WasmtimeLibraryFilename>libwasmtime.dylib</WasmtimeLibraryFilename>
<PackagePath>runtimes/osx-x64/native</PackagePath>
</WasmtimeDownload>
<WasmtimeDownload Include="Windows">
<URL>$(ReleaseURLBase)-windows-c-api.zip</URL>
<DownloadFolder>$(IntermediateOutputPath)wasmtime-windows</DownloadFolder>
<DownloadFilename>windows.zip</DownloadFilename>
<WasmtimeLibraryFilename>wasmtime.dll</WasmtimeLibraryFilename>
<PackagePath>runtimes/win-x64/native</PackagePath>
</WasmtimeDownload>
</ItemGroup>
<Message Text="Downloading Wasmtime release." Importance="High" />
<DownloadFile SourceUrl="%(WasmtimeDownload.URL)" DestinationFolder="%(WasmtimeDownload.DownloadFolder)" DestinationFileName="%(WasmtimeDownload.DownloadFilename)" SkipUnchangedFiles="true" />
<Message Text="Decompressing Wasmtime release." Importance="High" />
<Exec Command="tar --strip-components 1 -xvzf %(WasmtimeDownload.DownloadFilename)" WorkingDirectory="%(WasmtimeDownload.DownloadFolder)" StandardOutputImportance="Low" StandardErrorImportance="Low" />
<ItemGroup>
<Content Include="%(WasmtimeDownload.DownloadFolder)/lib/%(WasmtimeDownload.WasmtimeLibraryFilename)" Link="%(WasmtimeDownload.PackagePath)/%(WasmtimeDownload.WasmtimeLibraryFilename)">
<PackagePath>%(WasmtimeDownload.PackagePath)</PackagePath>
</Content>
</ItemGroup>
</Target>
</Project>

View File

@@ -1,40 +0,0 @@
using System;
using System.Runtime.Serialization;
using System.Text;
namespace Wasmtime
{
/// <summary>
/// The base type for Wasmtime exceptions.
/// </summary>
[System.Serializable]
public class WasmtimeException : Exception
{
/// <inheritdoc/>
public WasmtimeException() { }
/// <inheritdoc/>
public WasmtimeException(string message) : base(message) { }
/// <inheritdoc/>
public WasmtimeException(string message, Exception inner) : base(message, inner) { }
/// <inheritdoc/>
protected WasmtimeException(SerializationInfo info, StreamingContext context) : base(info, context) { }
internal static WasmtimeException FromOwnedError(IntPtr error)
{
unsafe
{
Interop.wasmtime_error_message(error, out var bytes);
var byteSpan = new ReadOnlySpan<byte>(bytes.data, checked((int)bytes.size));
var message = Encoding.UTF8.GetString(byteSpan);
Interop.wasm_byte_vec_delete(ref bytes);
Interop.wasmtime_error_delete(error);
return new WasmtimeException(message);
}
}
}
}

View File

@@ -1,39 +0,0 @@
using System;
using System.IO;
using Wasmtime;
namespace Wasmtime.Tests
{
public abstract class ModuleFixture : IDisposable
{
public ModuleFixture()
{
Host = new HostBuilder()
.WithMultiValue(true)
.WithReferenceTypes(true)
.Build();
Module = Host.LoadModuleText(Path.Combine("Modules", ModuleFileName));
}
public void Dispose()
{
if (!(Module is null))
{
Module.Dispose();
Module = null;
}
if (!(Host is null))
{
Host.Dispose();
Host = null;
}
}
public Host Host { get; set; }
public Module Module { get; set; }
protected abstract string ModuleFileName { get; }
}
}

View File

@@ -1,150 +0,0 @@
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.wat";
}
public class FunctionExportsTests : IClassFixture<FunctionExportsFixture>
{
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<object[]> GetFunctionExports()
{
yield return new object[] {
"no_params_no_results",
Array.Empty<ValueKind>(),
Array.Empty<ValueKind>()
};
yield return new object[] {
"one_i32_param_no_results",
new ValueKind[] {
ValueKind.Int32
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"one_i64_param_no_results",
new ValueKind[] {
ValueKind.Int64
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"one_f32_param_no_results",
new ValueKind[] {
ValueKind.Float32
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"one_f64_param_no_results",
new ValueKind[] {
ValueKind.Float64
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"one_param_of_each_type",
new ValueKind[] {
ValueKind.Int32,
ValueKind.Int64,
ValueKind.Float32,
ValueKind.Float64
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"no_params_one_i32_result",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Int32,
}
};
yield return new object[] {
"no_params_one_i64_result",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Int64,
}
};
yield return new object[] {
"no_params_one_f32_result",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Float32,
}
};
yield return new object[] {
"no_params_one_f64_result",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Float64,
}
};
yield return new object[] {
"one_result_of_each_type",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Int32,
ValueKind.Int64,
ValueKind.Float32,
ValueKind.Float64,
}
};
yield return new object[] {
"one_param_and_result_of_each_type",
new ValueKind[] {
ValueKind.Int32,
ValueKind.Int64,
ValueKind.Float32,
ValueKind.Float64,
},
new ValueKind[] {
ValueKind.Int32,
ValueKind.Int64,
ValueKind.Float32,
ValueKind.Float64,
}
};
}
}
}

View File

@@ -1,169 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class FunctionImportsFixture : ModuleFixture
{
protected override string ModuleFileName => "FunctionImports.wat";
}
public class FunctionImportsTests : IClassFixture<FunctionImportsFixture>
{
public FunctionImportsTests(FunctionImportsFixture fixture)
{
Fixture = fixture;
}
private FunctionImportsFixture Fixture { get; set; }
[Theory]
[MemberData(nameof(GetFunctionImports))]
public void ItHasTheExpectedFunctionImports(string importModule, string importName, ValueKind[] expectedParameters, ValueKind[] expectedResults)
{
var import = Fixture.Module.Imports.Functions.Where(f => f.ModuleName == importModule && f.Name == importName).FirstOrDefault();
import.Should().NotBeNull();
import.Parameters.Should().Equal(expectedParameters);
import.Results.Should().Equal(expectedResults);
}
[Fact]
public void ItHasTheExpectedNumberOfExportedFunctions()
{
GetFunctionImports().Count().Should().Be(Fixture.Module.Imports.Functions.Count);
}
public static IEnumerable<object[]> GetFunctionImports()
{
yield return new object[] {
"",
"no_params_no_results",
Array.Empty<ValueKind>(),
Array.Empty<ValueKind>()
};
yield return new object[] {
"",
"one_i32_param_no_results",
new ValueKind[] {
ValueKind.Int32
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"",
"one_i64_param_no_results",
new ValueKind[] {
ValueKind.Int64
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"",
"one_f32_param_no_results",
new ValueKind[] {
ValueKind.Float32
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"",
"one_f64_param_no_results",
new ValueKind[] {
ValueKind.Float64
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"",
"one_param_of_each_type",
new ValueKind[] {
ValueKind.Int32,
ValueKind.Int64,
ValueKind.Float32,
ValueKind.Float64
},
Array.Empty<ValueKind>()
};
yield return new object[] {
"",
"no_params_one_i32_result",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Int32,
}
};
yield return new object[] {
"",
"no_params_one_i64_result",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Int64,
}
};
yield return new object[] {
"",
"no_params_one_f32_result",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Float32,
}
};
yield return new object[] {
"",
"no_params_one_f64_result",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Float64,
}
};
yield return new object[] {
"",
"one_result_of_each_type",
Array.Empty<ValueKind>(),
new ValueKind[] {
ValueKind.Int32,
ValueKind.Int64,
ValueKind.Float32,
ValueKind.Float64,
}
};
yield return new object[] {
"",
"one_param_and_result_of_each_type",
new ValueKind[] {
ValueKind.Int32,
ValueKind.Int64,
ValueKind.Float32,
ValueKind.Float64,
},
new ValueKind[] {
ValueKind.Int32,
ValueKind.Int64,
ValueKind.Float32,
ValueKind.Float64,
}
};
yield return new object[] {
"other",
"function_from_module",
Array.Empty<ValueKind>(),
Array.Empty<ValueKind>(),
};
}
}
}

View File

@@ -1,72 +0,0 @@
using FluentAssertions;
using System;
using System.Linq;
using Xunit;
namespace Wasmtime.Tests
{
public class FunctionThunkingFixture : ModuleFixture
{
protected override string ModuleFileName => "FunctionThunking.wat";
}
public class FunctionThunkingTests : IClassFixture<FunctionThunkingFixture>
{
const string THROW_MESSAGE = "Test error message for wasmtime dotnet unit tests.";
public FunctionThunkingTests(FunctionThunkingFixture fixture)
{
Fixture = fixture;
Fixture.Host.DefineFunction("env", "add", (int x, int y) => x + y);
Fixture.Host.DefineFunction("env", "swap", (int x, int y) => (y, x));
Fixture.Host.DefineFunction("env", "do_throw", () => throw new Exception(THROW_MESSAGE));
Fixture.Host.DefineFunction("env", "check_string", (Caller caller, int address, int length) => {
caller.GetMemory("mem").ReadString(address, length).Should().Be("Hello World");
});
}
private FunctionThunkingFixture Fixture { get; }
[Fact]
public void ItBindsImportMethodsAndCallsThemCorrectly()
{
using dynamic instance = Fixture.Host.Instantiate(Fixture.Module);
int x = instance.add(40, 2);
x.Should().Be(42);
x = instance.add(22, 5);
x.Should().Be(27);
object[] results = instance.swap(10, 100);
results.Should().Equal(new object[] { 100, 10 });
instance.check_string();
// Collect garbage to make sure delegate function pointers pasted to wasmtime are rooted.
GC.Collect();
GC.WaitForPendingFinalizers();
x = instance.add(1970, 50);
x.Should().Be(2020);
results = instance.swap(2020, 1970);
results.Should().Equal(new object[] { 1970, 2020 });
instance.check_string();
}
[Fact]
public void ItPropagatesExceptionsToCallersViaTraps()
{
using dynamic instance = Fixture.Host.Instantiate(Fixture.Module);
Action action = () => instance.do_throw();
action
.Should()
.Throw<TrapException>()
.WithMessage(THROW_MESSAGE);
}
}
}

View File

@@ -1,214 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using FluentAssertions;
using Wasmtime;
using Xunit;
namespace Wasmtime.Tests
{
public class GlobalExportsFixture : ModuleFixture
{
protected override string ModuleFileName => "GlobalExports.wat";
}
public class GlobalExportsTests : IClassFixture<GlobalExportsFixture>
{
public GlobalExportsTests(GlobalExportsFixture fixture)
{
Fixture = fixture;
}
private GlobalExportsFixture Fixture { get; set; }
[Theory]
[MemberData(nameof(GetGlobalExports))]
public void ItHasTheExpectedGlobalExports(string exportName, ValueKind expectedKind, bool expectedMutable)
{
var export = Fixture.Module.Exports.Globals.Where(f => f.Name == exportName).FirstOrDefault();
export.Should().NotBeNull();
export.Kind.Should().Be(expectedKind);
export.IsMutable.Should().Be(expectedMutable);
}
[Fact]
public void ItHasTheExpectedNumberOfExportedGlobals()
{
GetGlobalExports().Count().Should().Be(Fixture.Module.Exports.Globals.Count);
}
[Fact]
public void ItCreatesExternsForTheGlobals()
{
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic dyn = instance;
var globals = instance.Externs.Globals;
globals.Count.Should().Be(8);
var i32 = globals[0];
i32.Name.Should().Be("global_i32");
i32.Kind.Should().Be(ValueKind.Int32);
i32.IsMutable.Should().Be(false);
i32.Value.Should().Be(0);
var i32Mut = globals[1];
i32Mut.Name.Should().Be("global_i32_mut");
i32Mut.Kind.Should().Be(ValueKind.Int32);
i32Mut.IsMutable.Should().Be(true);
i32Mut.Value.Should().Be(1);
i32Mut.Value = 11;
i32Mut.Value.Should().Be(11);
dyn.global_i32_mut = 12;
((int)dyn.global_i32_mut).Should().Be(12);
i32Mut.Value.Should().Be(12);
var i64 = globals[2];
i64.Name.Should().Be("global_i64");
i64.Kind.Should().Be(ValueKind.Int64);
i64.IsMutable.Should().Be(false);
i64.Value.Should().Be(2);
var i64Mut = globals[3];
i64Mut.Name.Should().Be("global_i64_mut");
i64Mut.Kind.Should().Be(ValueKind.Int64);
i64Mut.IsMutable.Should().Be(true);
i64Mut.Value.Should().Be(3);
i64Mut.Value = 13;
i64Mut.Value.Should().Be(13);
dyn.global_i64_mut = 14;
((long)dyn.global_i64_mut).Should().Be(14);
i64Mut.Value.Should().Be(14);
var f32 = globals[4];
f32.Name.Should().Be("global_f32");
f32.Kind.Should().Be(ValueKind.Float32);
f32.IsMutable.Should().Be(false);
f32.Value.Should().Be(4);
var f32Mut = globals[5];
f32Mut.Name.Should().Be("global_f32_mut");
f32Mut.Kind.Should().Be(ValueKind.Float32);
f32Mut.IsMutable.Should().Be(true);
f32Mut.Value.Should().Be(5);
f32Mut.Value = 15;
f32Mut.Value.Should().Be(15);
dyn.global_f32_mut = 16;
((float)dyn.global_f32_mut).Should().Be(16);
f32Mut.Value.Should().Be(16);
var f64 = globals[6];
f64.Name.Should().Be("global_f64");
f64.Kind.Should().Be(ValueKind.Float64);
f64.IsMutable.Should().Be(false);
f64.Value.Should().Be(6);
var f64Mut = globals[7];
f64Mut.Name.Should().Be("global_f64_mut");
f64Mut.Kind.Should().Be(ValueKind.Float64);
f64Mut.IsMutable.Should().Be(true);
f64Mut.Value.Should().Be(7);
f64Mut.Value = 17;
f64Mut.Value.Should().Be(17);
dyn.global_f64_mut = 17;
((double)dyn.global_f64_mut).Should().Be(17);
f64Mut.Value.Should().Be(17);
Action action = () => i32.Value = 0;
action
.Should()
.Throw<InvalidOperationException>()
.WithMessage("The value of global 'global_i32' cannot be modified.");
action = () => dyn.global_i32 = 0;
action
.Should()
.Throw<InvalidOperationException>()
.WithMessage("The value of global 'global_i32' cannot be modified.");
action = () => i64.Value = 0;
action
.Should()
.Throw<InvalidOperationException>()
.WithMessage("The value of global 'global_i64' cannot be modified.");
action = () => dyn.global_i64 = 0;
action
.Should()
.Throw<InvalidOperationException>()
.WithMessage("The value of global 'global_i64' cannot be modified.");
action = () => f32.Value = 0;
action
.Should()
.Throw<InvalidOperationException>()
.WithMessage("The value of global 'global_f32' cannot be modified.");
action = () => dyn.global_f32 = 0;
action
.Should()
.Throw<InvalidOperationException>()
.WithMessage("The value of global 'global_f32' cannot be modified.");
action = () => f64.Value = 0;
action
.Should()
.Throw<InvalidOperationException>()
.WithMessage("The value of global 'global_f64' cannot be modified.");
action = () => dyn.global_f64 = 0;
action
.Should()
.Throw<InvalidOperationException>()
.WithMessage("The value of global 'global_f64' cannot be modified.");
}
public static IEnumerable<object[]> GetGlobalExports()
{
yield return new object[] {
"global_i32",
ValueKind.Int32,
false
};
yield return new object[] {
"global_i32_mut",
ValueKind.Int32,
true
};
yield return new object[] {
"global_i64",
ValueKind.Int64,
false
};
yield return new object[] {
"global_i64_mut",
ValueKind.Int64,
true
};
yield return new object[] {
"global_f32",
ValueKind.Float32,
false
};
yield return new object[] {
"global_f32_mut",
ValueKind.Float32,
true
};
yield return new object[] {
"global_f64",
ValueKind.Float64,
false
};
yield return new object[] {
"global_f64_mut",
ValueKind.Float64,
true
};
}
}
}

View File

@@ -1,142 +0,0 @@
using System;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class GlobalImportBindingFixture : ModuleFixture
{
protected override string ModuleFileName => "GlobalImportBindings.wat";
}
public class GlobalImportBindingTests : IClassFixture<GlobalImportBindingFixture>
{
public GlobalImportBindingTests(GlobalImportBindingFixture fixture)
{
Fixture = fixture;
Fixture.Host.ClearDefinitions();
}
private GlobalImportBindingFixture Fixture { get; set; }
[Fact]
public void ItFailsToInstantiateWithMissingImport()
{
Action action = () => { using var instance = Fixture.Host.Instantiate(Fixture.Module); };
action
.Should()
.Throw<WasmtimeException>()
.WithMessage("unknown import: `::global_i32_mut` has not been defined");
}
[Fact]
public void ItFailsToDefineAGlobalWithInvalidType()
{
Action action = () => { Fixture.Host.DefineGlobal("", "global_i32_mut", "invalid"); };
action
.Should()
.Throw<WasmtimeException>()
.WithMessage("Global variables cannot be of type 'System.String'.");
}
[Fact]
public void ItFailsToInstantiateWithGlobalTypeMismatch()
{
Fixture.Host.DefineGlobal("", "global_i32_mut", 0L);
Action action = () => { using var instance = Fixture.Host.Instantiate(Fixture.Module); };
action
.Should()
.Throw<WasmtimeException>()
.WithMessage("incompatible import type for `::global_i32_mut` specified*");
}
[Fact]
public void ItFailsToInstantiateWhenGlobalIsNotMut()
{
Fixture.Host.DefineGlobal("", "global_i32_mut", 1);
Action action = () => { using var instance = Fixture.Host.Instantiate(Fixture.Module); };
action
.Should()
.Throw<WasmtimeException>()
.WithMessage("incompatible import type for `::global_i32_mut` specified*");
}
[Fact]
public void ItFailsToInstantiateWhenGlobalIsMut()
{
Fixture.Host.DefineMutableGlobal("", "global_i32_mut", 0);
Fixture.Host.DefineMutableGlobal("", "global_i32", 0);
Action action = () => { using var instance = Fixture.Host.Instantiate(Fixture.Module); };
action
.Should()
.Throw<WasmtimeException>()
.WithMessage("incompatible import type for `::global_i32` specified*");
}
[Fact]
public void ItBindsTheGlobalsCorrectly()
{
var global_i32_mut = Fixture.Host.DefineMutableGlobal("", "global_i32_mut", 0);
var global_i32 = Fixture.Host.DefineGlobal("", "global_i32", 1);
var global_i64_mut = Fixture.Host.DefineMutableGlobal("", "global_i64_mut", 2L);
var global_i64 = Fixture.Host.DefineGlobal("", "global_i64", 3L);
var global_f32_mut = Fixture.Host.DefineMutableGlobal("", "global_f32_mut", 4f);
var global_f32 = Fixture.Host.DefineGlobal("", "global_f32", 5f);
var global_f64_mut = Fixture.Host.DefineMutableGlobal("", "global_f64_mut", 6.0);
var global_f64 = Fixture.Host.DefineGlobal("", "global_f64", 7.0);
using dynamic instance = Fixture.Host.Instantiate(Fixture.Module);
global_i32_mut.Value.Should().Be(0);
((int)instance.get_global_i32_mut()).Should().Be(0);
global_i32.Value.Should().Be(1);
((int)instance.get_global_i32()).Should().Be(1);
global_i64_mut.Value.Should().Be(2);
((long)instance.get_global_i64_mut()).Should().Be(2);
global_i64.Value.Should().Be(3);
((long)instance.get_global_i64()).Should().Be(3);
global_f32_mut.Value.Should().Be(4);
((float)instance.get_global_f32_mut()).Should().Be(4);
global_f32.Value.Should().Be(5);
((float)instance.get_global_f32()).Should().Be(5);
global_f64_mut.Value.Should().Be(6);
((double)instance.get_global_f64_mut()).Should().Be(6);
global_f64.Value.Should().Be(7);
((double)instance.get_global_f64()).Should().Be(7);
global_i32_mut.Value = 10;
global_i32_mut.Value.Should().Be(10);
((int)instance.get_global_i32_mut()).Should().Be(10);
instance.set_global_i32_mut(11);
global_i32_mut.Value.Should().Be(11);
((int)instance.get_global_i32_mut()).Should().Be(11);
global_i64_mut.Value = 12;
global_i64_mut.Value.Should().Be(12);
((long)instance.get_global_i64_mut()).Should().Be(12);
instance.set_global_i64_mut(13);
global_i64_mut.Value.Should().Be(13);
((long)instance.get_global_i64_mut()).Should().Be(13);
global_f32_mut.Value = 14;
global_f32_mut.Value.Should().Be(14);
((float)instance.get_global_f32_mut()).Should().Be(14);
instance.set_global_f32_mut(15);
global_f32_mut.Value.Should().Be(15);
((float)instance.get_global_f32_mut()).Should().Be(15);
global_f64_mut.Value = 16;
global_f64_mut.Value.Should().Be(16);
((double)instance.get_global_f64_mut()).Should().Be(16);
instance.set_global_f64_mut(17);
global_f64_mut.Value.Should().Be(17);
((double)instance.get_global_f64_mut()).Should().Be(17);
}
}
}

View File

@@ -1,105 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class GlobalImportsFixture : ModuleFixture
{
protected override string ModuleFileName => "GlobalImports.wat";
}
public class GlobalImportsTests : IClassFixture<GlobalImportsFixture>
{
public GlobalImportsTests(GlobalImportsFixture fixture)
{
Fixture = fixture;
}
private GlobalImportsFixture Fixture { get; set; }
[Theory]
[MemberData(nameof(GetGlobalImports))]
public void ItHasTheExpectedGlobalImports(string importModule, string importName, ValueKind expectedKind, bool expectedMutable)
{
var import = Fixture.Module.Imports.Globals.Where(f => f.ModuleName == importModule && f.Name == importName).FirstOrDefault();
import.Should().NotBeNull();
import.Kind.Should().Be(expectedKind);
import.IsMutable.Should().Be(expectedMutable);
}
[Fact]
public void ItHasTheExpectedNumberOfExportedGlobals()
{
GetGlobalImports().Count().Should().Be(Fixture.Module.Imports.Globals.Count);
}
public static IEnumerable<object[]> GetGlobalImports()
{
yield return new object[] {
"",
"global_i32",
ValueKind.Int32,
false
};
yield return new object[] {
"",
"global_i32_mut",
ValueKind.Int32,
true
};
yield return new object[] {
"",
"global_i64",
ValueKind.Int64,
false
};
yield return new object[] {
"",
"global_i64_mut",
ValueKind.Int64,
true
};
yield return new object[] {
"",
"global_f32",
ValueKind.Float32,
false
};
yield return new object[] {
"",
"global_f32_mut",
ValueKind.Float32,
true
};
yield return new object[] {
"",
"global_f64",
ValueKind.Float64,
false
};
yield return new object[] {
"",
"global_f64_mut",
ValueKind.Float64,
true
};
yield return new object[] {
"other",
"global_from_module",
ValueKind.Int32,
false
};
}
}
}

View File

@@ -1,89 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class MemoryExportsFixture : ModuleFixture
{
protected override string ModuleFileName => "MemoryExports.wat";
}
public class MemoryExportsTests : IClassFixture<MemoryExportsFixture>
{
public MemoryExportsTests(MemoryExportsFixture fixture)
{
Fixture = fixture;
}
private MemoryExportsFixture Fixture { get; set; }
[Theory]
[MemberData(nameof(GetMemoryExports))]
public void ItHasTheExpectedMemoryExports(string exportName, uint expectedMinimum, uint expectedMaximum)
{
var export = Fixture.Module.Exports.Memories.Where(m => m.Name == exportName).FirstOrDefault();
export.Should().NotBeNull();
export.Minimum.Should().Be(expectedMinimum);
export.Maximum.Should().Be(expectedMaximum);
}
[Fact]
public void ItHasTheExpectedNumberOfExportedTables()
{
GetMemoryExports().Count().Should().Be(Fixture.Module.Exports.Memories.Count);
}
[Fact]
public void ItCreatesExternsForTheMemories()
{
using var instance = Fixture.Host.Instantiate(Fixture.Module);
instance.Externs.Memories.Count.Should().Be(1);
var memory = instance.Externs.Memories[0];
memory.ReadString(0, 11).Should().Be("Hello World");
int written = memory.WriteString(0, "WebAssembly Rocks!");
memory.ReadString(0, written).Should().Be("WebAssembly Rocks!");
memory.ReadByte(20).Should().Be(1);
memory.WriteByte(20, 11);
memory.ReadByte(20).Should().Be(11);
memory.ReadInt16(21).Should().Be(2);
memory.WriteInt16(21, 12);
memory.ReadInt16(21).Should().Be(12);
memory.ReadInt32(23).Should().Be(3);
memory.WriteInt32(23, 13);
memory.ReadInt32(23).Should().Be(13);
memory.ReadInt64(27).Should().Be(4);
memory.WriteInt64(27, 14);
memory.ReadInt64(27).Should().Be(14);
memory.ReadSingle(35).Should().Be(5);
memory.WriteSingle(35, 15);
memory.ReadSingle(35).Should().Be(15);
memory.ReadDouble(39).Should().Be(6);
memory.WriteDouble(39, 16);
memory.ReadDouble(39).Should().Be(16);
memory.ReadIntPtr(48).Should().Be((IntPtr)7);
memory.WriteIntPtr(48, (IntPtr)17);
memory.ReadIntPtr(48).Should().Be((IntPtr)17);
}
public static IEnumerable<object[]> GetMemoryExports()
{
yield return new object[] {
"mem",
1,
2
};
}
}
}

View File

@@ -1,81 +0,0 @@
using System;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class MemoryImportBindingFixture : ModuleFixture
{
protected override string ModuleFileName => "MemoryImportBinding.wat";
}
public class MemoryImportBindingTests : IClassFixture<MemoryImportBindingFixture>
{
public MemoryImportBindingTests(MemoryImportBindingFixture fixture)
{
Fixture = fixture;
Fixture.Host.ClearDefinitions();
}
private MemoryImportBindingFixture Fixture { get; set; }
[Fact]
public void ItFailsToInstantiateWithMissingImport()
{
Action action = () => { using var instance = Fixture.Host.Instantiate(Fixture.Module); };
action
.Should()
.Throw<WasmtimeException>()
.WithMessage("unknown import: `::mem` has not been defined");
}
[Fact]
public void ItBindsTheGlobalsCorrectly()
{
var mem = Fixture.Host.DefineMemory("", "mem");
using dynamic instance = Fixture.Host.Instantiate(Fixture.Module);
mem.ReadString(0, 11).Should().Be("Hello World");
int written = mem.WriteString(0, "WebAssembly Rocks!");
mem.ReadString(0, written).Should().Be("WebAssembly Rocks!");
mem.ReadByte(20).Should().Be(1);
mem.WriteByte(20, 11);
mem.ReadByte(20).Should().Be(11);
((byte)instance.ReadByte()).Should().Be(11);
mem.ReadInt16(21).Should().Be(2);
mem.WriteInt16(21, 12);
mem.ReadInt16(21).Should().Be(12);
((short)instance.ReadInt16()).Should().Be(12);
mem.ReadInt32(23).Should().Be(3);
mem.WriteInt32(23, 13);
mem.ReadInt32(23).Should().Be(13);
((int)instance.ReadInt32()).Should().Be(13);
mem.ReadInt64(27).Should().Be(4);
mem.WriteInt64(27, 14);
mem.ReadInt64(27).Should().Be(14);
((long)instance.ReadInt64()).Should().Be(14);
mem.ReadSingle(35).Should().Be(5);
mem.WriteSingle(35, 15);
mem.ReadSingle(35).Should().Be(15);
((float)instance.ReadFloat32()).Should().Be(15);
mem.ReadDouble(39).Should().Be(6);
mem.WriteDouble(39, 16);
mem.ReadDouble(39).Should().Be(16);
((double)instance.ReadFloat64()).Should().Be(16);
mem.ReadIntPtr(48).Should().Be((IntPtr)7);
mem.WriteIntPtr(48, (IntPtr)17);
mem.ReadIntPtr(48).Should().Be((IntPtr)17);
((IntPtr)instance.ReadIntPtr()).Should().Be((IntPtr)17);
}
}
}

View File

@@ -1,34 +0,0 @@
using System;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class MemoryImportFromModuleFixture : ModuleFixture
{
protected override string ModuleFileName => "MemoryImportFromModule.wat";
}
public class MemoryImportFromModuleTests : IClassFixture<MemoryImportFromModuleFixture>
{
public MemoryImportFromModuleTests(MemoryImportFromModuleFixture fixture)
{
Fixture = fixture;
}
private MemoryImportFromModuleFixture Fixture { get; set; }
[Fact]
public void ItHasTheExpectedImport()
{
Fixture.Module.Imports.Memories.Count.Should().Be(1);
var memory = Fixture.Module.Imports.Memories[0];
memory.ModuleName.Should().Be("js");
memory.Name.Should().Be("mem");
memory.Minimum.Should().Be(1);
memory.Maximum.Should().Be(2);
}
}
}

View File

@@ -1,34 +0,0 @@
using System;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class MemoryImportNoUpperBoundFixture : ModuleFixture
{
protected override string ModuleFileName => "MemoryImportNoUpperBound.wat";
}
public class MemoryImportNoUpperBoundTests : IClassFixture<MemoryImportNoUpperBoundFixture>
{
public MemoryImportNoUpperBoundTests(MemoryImportNoUpperBoundFixture fixture)
{
Fixture = fixture;
}
private MemoryImportNoUpperBoundFixture Fixture { get; set; }
[Fact]
public void ItHasTheExpectedImport()
{
Fixture.Module.Imports.Memories.Count.Should().Be(1);
var memory = Fixture.Module.Imports.Memories[0];
memory.ModuleName.Should().Be("");
memory.Name.Should().Be("mem");
memory.Minimum.Should().Be(1);
memory.Maximum.Should().Be(uint.MaxValue);
}
}
}

View File

@@ -1,34 +0,0 @@
using System;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class MemoryImportWithUpperBoundFixture : ModuleFixture
{
protected override string ModuleFileName => "MemoryImportWithUpperBound.wat";
}
public class MemoryImportWithUpperBoundTests : IClassFixture<MemoryImportWithUpperBoundFixture>
{
public MemoryImportWithUpperBoundTests(MemoryImportWithUpperBoundFixture fixture)
{
Fixture = fixture;
}
private MemoryImportWithUpperBoundFixture Fixture { get; set; }
[Fact]
public void ItHasTheExpectedImport()
{
Fixture.Module.Imports.Memories.Count.Should().Be(1);
var memory = Fixture.Module.Imports.Memories[0];
memory.ModuleName.Should().Be("");
memory.Name.Should().Be("mem");
memory.Minimum.Should().Be(10);
memory.Maximum.Should().Be(100);
}
}
}

View File

@@ -1,26 +0,0 @@
(module
(func $no_params_no_results)
(func $one_i32_param_no_results (param i32))
(func $one_i64_param_no_results (param i64))
(func $one_f32_param_no_results (param f32))
(func $one_f64_param_no_results (param f64))
(func $one_param_of_each_type (param i32 i64 f32 f64))
(func $no_params_one_i32_result (result i32) i32.const 0)
(func $no_params_one_i64_result (result i64) i64.const 0)
(func $no_params_one_f32_result (result f32) f32.const 0)
(func $no_params_one_f64_result (result f64) f64.const 0)
(func $one_result_of_each_type (result i32 i64 f32 f64) i32.const 0 i64.const 0 f32.const 0 f64.const 0)
(func $one_param_and_result_of_each_type (param i32 i64 f32 f64) (result i32 i64 f32 f64) i32.const 0 i64.const 0 f32.const 0 f64.const 0)
(export "no_params_no_results" (func $no_params_no_results))
(export "one_i32_param_no_results" (func $one_i32_param_no_results))
(export "one_i64_param_no_results" (func $one_i64_param_no_results))
(export "one_f32_param_no_results" (func $one_f32_param_no_results))
(export "one_f64_param_no_results" (func $one_f64_param_no_results))
(export "one_param_of_each_type" (func $one_param_of_each_type))
(export "no_params_one_i32_result" (func $no_params_one_i32_result))
(export "no_params_one_i64_result" (func $no_params_one_i64_result))
(export "no_params_one_f32_result" (func $no_params_one_f32_result))
(export "no_params_one_f64_result" (func $no_params_one_f64_result))
(export "one_result_of_each_type" (func $one_result_of_each_type))
(export "one_param_and_result_of_each_type" (func $one_param_and_result_of_each_type))
)

View File

@@ -1,27 +0,0 @@
(module
(type $t0 (func))
(type $t1 (func (param i32)))
(type $t2 (func (param i64)))
(type $t3 (func (param f32)))
(type $t4 (func (param f64)))
(type $t5 (func (param i32 i64 f32 f64)))
(type $t6 (func (result i32)))
(type $t7 (func (result i64)))
(type $t8 (func (result f32)))
(type $t9 (func (result f64)))
(type $t10 (func (result i32 i64 f32 f64)))
(type $t11 (func (param i32 i64 f32 f64) (result i32 i64 f32 f64)))
(import "" "no_params_no_results" (func $.f0 (type $t0)))
(import "" "one_i32_param_no_results" (func $.f1 (type $t1)))
(import "" "one_i64_param_no_results" (func $.f2 (type $t2)))
(import "" "one_f32_param_no_results" (func $.f3 (type $t3)))
(import "" "one_f64_param_no_results" (func $.f4 (type $t4)))
(import "" "one_param_of_each_type" (func $.f5 (type $t5)))
(import "" "no_params_one_i32_result" (func $.f6 (type $t6)))
(import "" "no_params_one_i64_result" (func $.f7 (type $t7)))
(import "" "no_params_one_f32_result" (func $.f8 (type $t8)))
(import "" "no_params_one_f64_result" (func $.f9 (type $t9)))
(import "" "one_result_of_each_type" (func $.f10 (type $t10)))
(import "" "one_param_and_result_of_each_type" (func $.f11 (type $t11)))
(import "other" "function_from_module" (func $.f12 (type $t0)))
)

View File

@@ -1,24 +0,0 @@
(module
(import "env" "add" (func $env.add (param i32 i32) (result i32)))
(import "env" "swap" (func $env.swap (param i32 i32) (result i32 i32)))
(import "env" "do_throw" (func $env.do_throw))
(import "env" "check_string" (func $env.check_string (param i32 i32)))
(memory (export "mem") 1)
(export "add" (func $add))
(export "swap" (func $swap))
(export "do_throw" (func $do_throw))
(export "check_string" (func $check_string))
(func $add (param i32 i32) (result i32)
(call $env.add (local.get 0) (local.get 1))
)
(func $swap (param i32 i32) (result i32 i32)
(call $env.swap (local.get 0) (local.get 1))
)
(func $do_throw
(call $env.do_throw)
)
(func $check_string
(call $env.check_string (i32.const 0) (i32.const 11))
)
(data (i32.const 0) "Hello World")
)

View File

@@ -1,18 +0,0 @@
(module
(global $g1 i32 (i32.const 0))
(global $g2 (mut i32) (i32.const 1))
(global $g3 i64 (i64.const 2))
(global $g4 (mut i64) (i64.const 3))
(global $g5 f32 (f32.const 4))
(global $g6 (mut f32) (f32.const 5))
(global $g7 f64 (f64.const 6))
(global $g8 (mut f64) (f64.const 7))
(export "global_i32" (global $g1))
(export "global_i32_mut" (global $g2))
(export "global_i64" (global $g3))
(export "global_i64_mut" (global $g4))
(export "global_f32" (global $g5))
(export "global_f32_mut" (global $g6))
(export "global_f64" (global $g7))
(export "global_f64_mut" (global $g8))
)

View File

@@ -1,22 +0,0 @@
(module
(import "" "global_i32_mut" (global $global_i32_mut (mut i32)))
(import "" "global_i32" (global $global_i32 i32))
(import "" "global_i64_mut" (global $global_i64_mut (mut i64)))
(import "" "global_i64" (global $global_i64 i64))
(import "" "global_f32_mut" (global $global_f32_mut (mut f32)))
(import "" "global_f32" (global $global_f32 f32))
(import "" "global_f64_mut" (global $global_f64_mut (mut f64)))
(import "" "global_f64" (global $global_f64 f64))
(func (export "get_global_i32_mut") (result i32) (global.get $global_i32_mut))
(func (export "get_global_i32") (result i32) (global.get $global_i32))
(func (export "set_global_i32_mut") (param i32) (global.set $global_i32_mut (local.get 0)))
(func (export "get_global_i64_mut") (result i64) (global.get $global_i64_mut))
(func (export "get_global_i64") (result i64) (global.get $global_i64))
(func (export "set_global_i64_mut") (param i64) (global.set $global_i64_mut (local.get 0)))
(func (export "get_global_f32_mut") (result f32) (global.get $global_f32_mut))
(func (export "get_global_f32") (result f32) (global.get $global_f32))
(func (export "set_global_f32_mut") (param f32) (global.set $global_f32_mut (local.get 0)))
(func (export "get_global_f64_mut") (result f64) (global.get $global_f64_mut))
(func (export "get_global_f64") (result f64) (global.get $global_f64))
(func (export "set_global_f64_mut") (param f64) (global.set $global_f64_mut (local.get 0)))
)

View File

@@ -1,11 +0,0 @@
(module
(global $g1 (import "" "global_i32") i32)
(global $g2 (import "" "global_i32_mut") (mut i32))
(global $g3 (import "" "global_i64") i64)
(global $g4 (import "" "global_i64_mut") (mut i64))
(global $g5 (import "" "global_f32") f32)
(global $g6 (import "" "global_f32_mut") (mut f32))
(global $g7 (import "" "global_f64") f64)
(global $g8 (import "" "global_f64_mut") (mut f64))
(global $g9 (import "other" "global_from_module") i32)
)

View File

@@ -1,11 +0,0 @@
(module
(memory (export "mem") 1 2)
(data (i32.const 0) "Hello World")
(data (i32.const 20) "\01")
(data (i32.const 21) "\02\00")
(data (i32.const 23) "\03\00\00\00")
(data (i32.const 27) "\04\00\00\00\00\00\00\00")
(data (i32.const 35) "\00\00\a0\40")
(data (i32.const 39) "\00\00\00\00\00\00\18\40")
(data (i32.const 48) "\07\00\00\00\00\00\00\00")
)

View File

@@ -1,39 +0,0 @@
(module
(import "" "mem" (memory 1))
(data (i32.const 0) "Hello World")
(data (i32.const 20) "\01")
(data (i32.const 21) "\02\00")
(data (i32.const 23) "\03\00\00\00")
(data (i32.const 27) "\04\00\00\00\00\00\00\00")
(data (i32.const 35) "\00\00\a0\40")
(data (i32.const 39) "\00\00\00\00\00\00\18\40")
(data (i32.const 48) "\07\00\00\00\00\00\00\00")
(func (export "ReadByte") (result i32)
i32.const 20
i32.load8_s
)
(func (export "ReadInt16") (result i32)
i32.const 21
i32.load16_s
)
(func (export "ReadInt32") (result i32)
i32.const 23
i32.load
)
(func (export "ReadInt64") (result i64)
i32.const 27
i64.load
)
(func (export "ReadFloat32") (result f32)
i32.const 35
f32.load
)
(func (export "ReadFloat64") (result f64)
i32.const 39
f64.load
)
(func (export "ReadIntPtr") (result i64)
i32.const 48
i64.load
)
)

View File

@@ -1,3 +0,0 @@
(module
(import "js" "mem" (memory 1 2))
)

View File

@@ -1,3 +0,0 @@
(module
(import "" "mem" (memory 1))
)

View File

@@ -1,3 +0,0 @@
(module
(import "" "mem" (memory 10 100))
)

View File

@@ -1,8 +0,0 @@
(module
(table $t0 1 10 funcref)
(table $t1 10 anyref)
(table $t2 100 1000 funcref)
(export "table1" (table $t0))
(export "table2" (table $t1))
(export "table3" (table $t2))
)

View File

@@ -1,5 +0,0 @@
(module
(import "" "table1" (table $t1 10 funcref))
(import "" "table2" (table $t2 15 anyref))
(import "other" "table3" (table $t3 1 funcref))
)

View File

@@ -1,66 +0,0 @@
(module
(type $t0 (func (param i32 i32) (result i32)))
(type $t1 (func (param i32 i32 i32 i32) (result i32)))
(type $t2 (func (param i32) (result i32)))
(type $t3 (func (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32)))
(import "wasi_snapshot_preview1" "environ_sizes_get" (func $wasi_snapshot_preview1.environ_sizes_get (type $t0)))
(import "wasi_snapshot_preview1" "environ_get" (func $wasi_snapshot_preview1.environ_get (type $t0)))
(import "wasi_snapshot_preview1" "args_sizes_get" (func $wasi_snapshot_preview1.args_sizes_get (type $t0)))
(import "wasi_snapshot_preview1" "args_get" (func $wasi_snapshot_preview1.args_get (type $t0)))
(import "wasi_snapshot_preview1" "fd_write" (func $wasi_snapshot_preview1.fd_write (type $t1)))
(import "wasi_snapshot_preview1" "fd_read" (func $wasi_snapshot_preview1.fd_read (type $t1)))
(import "wasi_snapshot_preview1" "fd_close" (func $wasi_snapshot_preview1.fd_close (type $t2)))
(import "wasi_snapshot_preview1" "path_open" (func $wasi_snapshot_preview1.path_open (type $t3)))
(memory $memory 1)
(export "memory" (memory 0))
(func $call_environ_sizes_get (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
local.get $p0
local.get $p1
call $wasi_snapshot_preview1.environ_sizes_get)
(func $call_environ_get (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
local.get $p0
local.get $p1
call $wasi_snapshot_preview1.environ_get)
(func $call_args_sizes_get (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
local.get $p0
local.get $p1
call $wasi_snapshot_preview1.args_sizes_get)
(func $call_args_get (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
local.get $p0
local.get $p1
call $wasi_snapshot_preview1.args_get)
(func $call_fd_write (type $t1) (param $p0 i32) (param $p1 i32) (param $p2 i32) (param $p3 i32) (result i32)
local.get $p0
local.get $p1
local.get $p2
local.get $p3
call $wasi_snapshot_preview1.fd_write)
(func $call_fd_read (type $t1) (param $p0 i32) (param $p1 i32) (param $p2 i32) (param $p3 i32) (result i32)
local.get $p0
local.get $p1
local.get $p2
local.get $p3
call $wasi_snapshot_preview1.fd_read)
(func $call_fd_close (type $t2) (param $p0 i32) (result i32)
local.get $p0
call $wasi_snapshot_preview1.fd_close)
(func $call_path_open (type $t3) (param $p0 i32) (param $p1 i32) (param $p2 i32) (param $p3 i32) (param $p4 i32) (param $p5 i64) (param $p6 i64) (param $p7 i32) (param $p8 i32) (result i32)
local.get $p0
local.get $p1
local.get $p2
local.get $p3
local.get $p4
local.get $p5
local.get $p6
local.get $p7
local.get $p8
call $wasi_snapshot_preview1.path_open)
(export "call_environ_sizes_get" (func $call_environ_sizes_get))
(export "call_environ_get" (func $call_environ_get))
(export "call_args_sizes_get" (func $call_args_sizes_get))
(export "call_args_get" (func $call_args_get))
(export "call_fd_write" (func $call_fd_write))
(export "call_fd_read" (func $call_fd_read))
(export "call_fd_close" (func $call_fd_close))
(export "call_path_open" (func $call_path_open))
)

View File

@@ -1,66 +0,0 @@
(module
(type $t0 (func (param i32 i32) (result i32)))
(type $t1 (func (param i32 i32 i32 i32) (result i32)))
(type $t2 (func (param i32) (result i32)))
(type $t3 (func (param i32 i32 i32 i32 i32 i64 i64 i32 i32) (result i32)))
(import "wasi_unstable" "environ_sizes_get" (func $wasi_unstable.environ_sizes_get (type $t0)))
(import "wasi_unstable" "environ_get" (func $wasi_unstable.environ_get (type $t0)))
(import "wasi_unstable" "args_sizes_get" (func $wasi_unstable.args_sizes_get (type $t0)))
(import "wasi_unstable" "args_get" (func $wasi_unstable.args_get (type $t0)))
(import "wasi_unstable" "fd_write" (func $wasi_unstable.fd_write (type $t1)))
(import "wasi_unstable" "fd_read" (func $wasi_unstable.fd_read (type $t1)))
(import "wasi_unstable" "fd_close" (func $wasi_unstable.fd_close (type $t2)))
(import "wasi_unstable" "path_open" (func $wasi_unstable.path_open (type $t3)))
(memory $memory 1)
(export "memory" (memory 0))
(func $call_environ_sizes_get (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
local.get $p0
local.get $p1
call $wasi_unstable.environ_sizes_get)
(func $call_environ_get (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
local.get $p0
local.get $p1
call $wasi_unstable.environ_get)
(func $call_args_sizes_get (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
local.get $p0
local.get $p1
call $wasi_unstable.args_sizes_get)
(func $call_args_get (type $t0) (param $p0 i32) (param $p1 i32) (result i32)
local.get $p0
local.get $p1
call $wasi_unstable.args_get)
(func $call_fd_write (type $t1) (param $p0 i32) (param $p1 i32) (param $p2 i32) (param $p3 i32) (result i32)
local.get $p0
local.get $p1
local.get $p2
local.get $p3
call $wasi_unstable.fd_write)
(func $call_fd_read (type $t1) (param $p0 i32) (param $p1 i32) (param $p2 i32) (param $p3 i32) (result i32)
local.get $p0
local.get $p1
local.get $p2
local.get $p3
call $wasi_unstable.fd_read)
(func $call_fd_close (type $t2) (param $p0 i32) (result i32)
local.get $p0
call $wasi_unstable.fd_close)
(func $call_path_open (type $t3) (param $p0 i32) (param $p1 i32) (param $p2 i32) (param $p3 i32) (param $p4 i32) (param $p5 i64) (param $p6 i64) (param $p7 i32) (param $p8 i32) (result i32)
local.get $p0
local.get $p1
local.get $p2
local.get $p3
local.get $p4
local.get $p5
local.get $p6
local.get $p7
local.get $p8
call $wasi_unstable.path_open)
(export "call_environ_sizes_get" (func $call_environ_sizes_get))
(export "call_environ_get" (func $call_environ_get))
(export "call_args_sizes_get" (func $call_args_sizes_get))
(export "call_args_get" (func $call_args_get))
(export "call_fd_write" (func $call_fd_write))
(export "call_fd_read" (func $call_fd_read))
(export "call_fd_close" (func $call_fd_close))
(export "call_path_open" (func $call_path_open))
)

View File

@@ -1,64 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class TableExportsFixture : ModuleFixture
{
protected override string ModuleFileName => "TableExports.wat";
}
public class TableExportsTests : IClassFixture<TableExportsFixture>
{
public TableExportsTests(TableExportsFixture fixture)
{
Fixture = fixture;
}
private TableExportsFixture Fixture { get; set; }
[Theory]
[MemberData(nameof(GetTableExports))]
public void ItHasTheExpectedTableExports(string exportName, ValueKind expectedKind, uint expectedMinimum, uint expectedMaximum)
{
var export = Fixture.Module.Exports.Tables.Where(f => f.Name == exportName).FirstOrDefault();
export.Should().NotBeNull();
export.Kind.Should().Be(expectedKind);
export.Minimum.Should().Be(expectedMinimum);
export.Maximum.Should().Be(expectedMaximum);
}
[Fact]
public void ItHasTheExpectedNumberOfExportedTables()
{
GetTableExports().Count().Should().Be(Fixture.Module.Exports.Tables.Count);
}
public static IEnumerable<object[]> GetTableExports()
{
yield return new object[] {
"table1",
ValueKind.FuncRef,
1,
10
};
yield return new object[] {
"table2",
ValueKind.AnyRef,
10,
uint.MaxValue
};
yield return new object[] {
"table3",
ValueKind.FuncRef,
100,
1000
};
}
}
}

View File

@@ -1,67 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class TableImportsFixture : ModuleFixture
{
protected override string ModuleFileName => "TableImports.wat";
}
public class TableImportsTests : IClassFixture<TableImportsFixture>
{
public TableImportsTests(TableImportsFixture fixture)
{
Fixture = fixture;
}
private TableImportsFixture Fixture { get; set; }
[Theory]
[MemberData(nameof(GetTableImports))]
public void ItHasTheExpectedTableImports(string importModule, string importName, ValueKind expectedKind, uint expectedMinimum, uint expectedMaximum)
{
var import = Fixture.Module.Imports.Tables.Where(f => f.ModuleName == importModule && f.Name == importName).FirstOrDefault();
import.Should().NotBeNull();
import.Kind.Should().Be(expectedKind);
import.Minimum.Should().Be(expectedMinimum);
import.Maximum.Should().Be(expectedMaximum);
}
[Fact]
public void ItHasTheExpectedNumberOfExportedTables()
{
GetTableImports().Count().Should().Be(Fixture.Module.Imports.Tables.Count);
}
public static IEnumerable<object[]> GetTableImports()
{
yield return new object[] {
"",
"table1",
ValueKind.FuncRef,
10,
uint.MaxValue
};
yield return new object[] {
"",
"table2",
ValueKind.AnyRef,
15,
uint.MaxValue
};
yield return new object[] {
"other",
"table3",
ValueKind.FuncRef,
1,
uint.MaxValue
};
}
}
}

View File

@@ -1,24 +0,0 @@
using System;
using System.IO;
namespace Wasmtime.Tests
{
internal class TempFile : IDisposable
{
public TempFile()
{
Path = System.IO.Path.GetTempFileName();
}
public void Dispose()
{
if (Path != null)
{
File.Delete(Path);
Path = null;
}
}
public string Path { get; private set; }
}
}

View File

@@ -1,257 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class WasiSnapshot0Fixture : ModuleFixture
{
protected override string ModuleFileName => "WasiSnapshot0.wat";
}
public class WasiSnapshot0Tests : IClassFixture<WasiSnapshot0Fixture>
{
public WasiSnapshot0Tests(WasiSnapshot0Fixture fixture)
{
Fixture = fixture;
Fixture.Host.ClearDefinitions();
}
private WasiSnapshot0Fixture Fixture { get; set; }
[Fact]
public void ItHasNoEnvironmentByDefault()
{
Fixture.Host.DefineWasi("wasi_unstable");
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
Assert.Equal(0, inst.call_environ_sizes_get(0, 4));
Assert.Equal(0, memory.ReadInt32(0));
Assert.Equal(0, memory.ReadInt32(4));
}
[Fact]
public void ItHasSpecifiedEnvironment()
{
var env = new Dictionary<string, string>() {
{"FOO", "BAR"},
{"WASM", "IS"},
{"VERY", "COOL"},
};
var config = new WasiConfiguration()
.WithEnvironmentVariables(env.Select(kvp => (kvp.Key, kvp.Value)));
Fixture.Host.DefineWasi("wasi_unstable", config);
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
Assert.Equal(0, inst.call_environ_sizes_get(0, 4));
Assert.Equal(env.Count, memory.ReadInt32(0));
Assert.Equal(env.Sum(kvp => kvp.Key.Length + kvp.Value.Length + 2), memory.ReadInt32(4));
Assert.Equal(0, inst.call_environ_get(0, 4 * env.Count));
for (int i = 0; i < env.Count; ++i)
{
var kvp = memory.ReadNullTerminatedString(memory.ReadInt32(i * 4)).Split("=");
Assert.Equal(env[kvp[0]], kvp[1]);
}
}
[Fact]
public void ItInheritsEnvironment()
{
var config = new WasiConfiguration()
.WithInheritedEnvironment();
Fixture.Host.DefineWasi("wasi_unstable", config);
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
Assert.Equal(0, inst.call_environ_sizes_get(0, 4));
Assert.Equal(Environment.GetEnvironmentVariables().Keys.Count, memory.ReadInt32(0));
}
[Fact]
public void ItHasNoArgumentsByDefault()
{
Fixture.Host.DefineWasi("wasi_unstable");
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
Assert.Equal(0, inst.call_args_sizes_get(0, 4));
Assert.Equal(0, memory.ReadInt32(0));
Assert.Equal(0, memory.ReadInt32(4));
}
[Fact]
public void ItHasSpecifiedArguments()
{
var args = new List<string>() {
"WASM",
"IS",
"VERY",
"COOL"
};
var config = new WasiConfiguration()
.WithArgs(args);
Fixture.Host.DefineWasi("wasi_unstable", config);
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
Assert.Equal(0, inst.call_args_sizes_get(0, 4));
Assert.Equal(args.Count, memory.ReadInt32(0));
Assert.Equal(args.Sum(a => a.Length + 1), memory.ReadInt32(4));
Assert.Equal(0, inst.call_args_get(0, 4 * args.Count));
for (int i = 0; i < args.Count; ++i)
{
var arg = memory.ReadNullTerminatedString(memory.ReadInt32(i * 4));
Assert.Equal(args[i], arg);
}
}
[Fact]
public void ItInheritsArguments()
{
var config = new WasiConfiguration()
.WithInheritedArgs();
Fixture.Host.DefineWasi("wasi_unstable", config);
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
Assert.Equal(0, inst.call_args_sizes_get(0, 4));
Assert.Equal(Environment.GetCommandLineArgs().Length, memory.ReadInt32(0));
}
[Fact]
public void ItSetsStdIn()
{
const string MESSAGE = "WASM IS VERY COOL";
using var file = new TempFile();
File.WriteAllText(file.Path, MESSAGE);
var config = new WasiConfiguration()
.WithStandardInput(file.Path);
Fixture.Host.DefineWasi("wasi_unstable", config);
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
memory.WriteInt32(0, 8);
memory.WriteInt32(4, MESSAGE.Length);
Assert.Equal(0, inst.call_fd_read(0, 0, 1, 32));
Assert.Equal(MESSAGE.Length, memory.ReadInt32(32));
Assert.Equal(MESSAGE, memory.ReadString(8, MESSAGE.Length));
}
[Theory]
[InlineData(1)]
[InlineData(2)]
public void ItSetsStdOutAndStdErr(int fd)
{
const string MESSAGE = "WASM IS VERY COOL";
using var file = new TempFile();
var config = new WasiConfiguration();
if (fd == 1)
{
config.WithStandardOutput(file.Path);
}
else if (fd == 2)
{
config.WithStandardError(file.Path);
}
Fixture.Host.DefineWasi("wasi_unstable", config);
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
memory.WriteInt32(0, 8);
memory.WriteInt32(4, MESSAGE.Length);
memory.WriteString(8, MESSAGE);
Assert.Equal(0, inst.call_fd_write(fd, 0, 1, 32));
Assert.Equal(MESSAGE.Length, memory.ReadInt32(32));
Assert.Equal(0, inst.call_fd_close(fd));
Assert.Equal(MESSAGE, File.ReadAllText(file.Path));
}
[Fact]
public void ItSetsPreopenDirectories()
{
const string MESSAGE = "WASM IS VERY COOL";
using var file = new TempFile();
var config = new WasiConfiguration()
.WithPreopenedDirectory(Path.GetDirectoryName(file.Path), "/foo");
Fixture.Host.DefineWasi("wasi_unstable", config);
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
var fileName = Path.GetFileName(file.Path);
memory.WriteString(0, fileName);
Assert.Equal(0, inst.call_path_open(
3,
0,
0,
fileName.Length,
0,
0x40 /* RIGHTS_FD_WRITE */,
0,
0,
64
)
);
var fileFd = (int) memory.ReadInt32(64);
Assert.True(fileFd > 3);
memory.WriteInt32(0, 8);
memory.WriteInt32(4, MESSAGE.Length);
memory.WriteString(8, MESSAGE);
Assert.Equal(0, inst.call_fd_write(fileFd, 0, 1, 64));
Assert.Equal(MESSAGE.Length, memory.ReadInt32(64));
Assert.Equal(0, inst.call_fd_close(fileFd));
Assert.Equal(MESSAGE, File.ReadAllText(file.Path));
}
}
}

View File

@@ -1,256 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.IO;
using FluentAssertions;
using Xunit;
namespace Wasmtime.Tests
{
public class WasiFixture : ModuleFixture
{
protected override string ModuleFileName => "Wasi.wat";
}
public class WasiTests : IClassFixture<WasiFixture>
{
public WasiTests(WasiFixture fixture)
{
Fixture = fixture;
}
private WasiFixture Fixture { get; set; }
[Fact]
public void ItHasNoEnvironmentByDefault()
{
Fixture.Host.DefineWasi("wasi_snapshot_preview1");
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
Assert.Equal(0, inst.call_environ_sizes_get(0, 4));
Assert.Equal(0, memory.ReadInt32(0));
Assert.Equal(0, memory.ReadInt32(4));
}
[Fact]
public void ItHasSpecifiedEnvironment()
{
var env = new Dictionary<string, string>() {
{"FOO", "BAR"},
{"WASM", "IS"},
{"VERY", "COOL"},
};
var config = new WasiConfiguration()
.WithEnvironmentVariables(env.Select(kvp => (kvp.Key, kvp.Value)));
Fixture.Host.DefineWasi("wasi_snapshot_preview1", config);
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
Assert.Equal(0, inst.call_environ_sizes_get(0, 4));
Assert.Equal(env.Count, memory.ReadInt32(0));
Assert.Equal(env.Sum(kvp => kvp.Key.Length + kvp.Value.Length + 2), memory.ReadInt32(4));
Assert.Equal(0, inst.call_environ_get(0, 4 * env.Count));
for (int i = 0; i < env.Count; ++i)
{
var kvp = memory.ReadNullTerminatedString(memory.ReadInt32(i * 4)).Split("=");
Assert.Equal(env[kvp[0]], kvp[1]);
}
}
[Fact]
public void ItInheritsEnvironment()
{
var config = new WasiConfiguration()
.WithInheritedEnvironment();
Fixture.Host.DefineWasi("wasi_snapshot_preview1", config);
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
Assert.Equal(0, inst.call_environ_sizes_get(0, 4));
Assert.Equal(Environment.GetEnvironmentVariables().Keys.Count, memory.ReadInt32(0));
}
[Fact]
public void ItHasNoArgumentsByDefault()
{
Fixture.Host.DefineWasi("wasi_snapshot_preview1");
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
Assert.Equal(0, inst.call_args_sizes_get(0, 4));
Assert.Equal(0, memory.ReadInt32(0));
Assert.Equal(0, memory.ReadInt32(4));
}
[Fact]
public void ItHasSpecifiedArguments()
{
var args = new List<string>() {
"WASM",
"IS",
"VERY",
"COOL"
};
var config = new WasiConfiguration()
.WithArgs(args);
Fixture.Host.DefineWasi("wasi_snapshot_preview1", config);
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
Assert.Equal(0, inst.call_args_sizes_get(0, 4));
Assert.Equal(args.Count, memory.ReadInt32(0));
Assert.Equal(args.Sum(a => a.Length + 1), memory.ReadInt32(4));
Assert.Equal(0, inst.call_args_get(0, 4 * args.Count));
for (int i = 0; i < args.Count; ++i)
{
var arg = memory.ReadNullTerminatedString(memory.ReadInt32(i * 4));
Assert.Equal(args[i], arg);
}
}
[Fact]
public void ItInheritsArguments()
{
var config = new WasiConfiguration()
.WithInheritedArgs();
Fixture.Host.DefineWasi("wasi_snapshot_preview1", config);
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
Assert.Equal(0, inst.call_args_sizes_get(0, 4));
Assert.Equal(Environment.GetCommandLineArgs().Length, memory.ReadInt32(0));
}
[Fact]
public void ItSetsStdIn()
{
const string MESSAGE = "WASM IS VERY COOL";
using var file = new TempFile();
File.WriteAllText(file.Path, MESSAGE);
var config = new WasiConfiguration()
.WithStandardInput(file.Path);
Fixture.Host.DefineWasi("wasi_snapshot_preview1", config);
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
memory.WriteInt32(0, 8);
memory.WriteInt32(4, MESSAGE.Length);
Assert.Equal(0, inst.call_fd_read(0, 0, 1, 32));
Assert.Equal(MESSAGE.Length, memory.ReadInt32(32));
Assert.Equal(MESSAGE, memory.ReadString(8, MESSAGE.Length));
}
[Theory]
[InlineData(1)]
[InlineData(2)]
public void ItSetsStdOutAndStdErr(int fd)
{
const string MESSAGE = "WASM IS VERY COOL";
using var file = new TempFile();
var config = new WasiConfiguration();
if (fd == 1)
{
config.WithStandardOutput(file.Path);
}
else if (fd == 2)
{
config.WithStandardError(file.Path);
}
Fixture.Host.DefineWasi("wasi_snapshot_preview1", config);
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
memory.WriteInt32(0, 8);
memory.WriteInt32(4, MESSAGE.Length);
memory.WriteString(8, MESSAGE);
Assert.Equal(0, inst.call_fd_write(fd, 0, 1, 32));
Assert.Equal(MESSAGE.Length, memory.ReadInt32(32));
Assert.Equal(0, inst.call_fd_close(fd));
Assert.Equal(MESSAGE, File.ReadAllText(file.Path));
}
[Fact]
public void ItSetsPreopenDirectories()
{
const string MESSAGE = "WASM IS VERY COOL";
using var file = new TempFile();
var config = new WasiConfiguration()
.WithPreopenedDirectory(Path.GetDirectoryName(file.Path), "/foo");
Fixture.Host.DefineWasi("wasi_snapshot_preview1", config);
using var instance = Fixture.Host.Instantiate(Fixture.Module);
dynamic inst = instance;
var memory = instance.Externs.Memories[0];
var fileName = Path.GetFileName(file.Path);
memory.WriteString(0, fileName);
Assert.Equal(0, inst.call_path_open(
3,
0,
0,
fileName.Length,
0,
0x40 /* RIGHTS_FD_WRITE */,
0,
0,
64
)
);
var fileFd = (int) memory.ReadInt32(64);
Assert.True(fileFd > 3);
memory.WriteInt32(0, 8);
memory.WriteInt32(4, MESSAGE.Length);
memory.WriteString(8, MESSAGE);
Assert.Equal(0, inst.call_fd_write(fileFd, 0, 1, 64));
Assert.Equal(MESSAGE.Length, memory.ReadInt32(64));
Assert.Equal(0, inst.call_fd_close(fileFd));
Assert.Equal(MESSAGE, File.ReadAllText(file.Path));
}
}
}

View File

@@ -1,33 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="5.9.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
<PackageReference Include="xunit" Version="2.4.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
<PackageReference Include="coverlet.collector" Version="1.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\src\Wasmtime.csproj" />
</ItemGroup>
<!-- This is needed as we're not referencing Wasmtime as a package. -->
<Target Name="BuildWasmtime" BeforeTargets="AssignTargetPaths">
<Message Text="Building Wasmtime from source." Importance="High" />
<Exec Command="$(BuildWasmtimeCommand)" StandardOutputImportance="Low" StandardErrorImportance="Low" />
<ItemGroup>
<None Include="$(WasmtimeOutputPath)/$(WasmtimeLibraryFilename)" Link="$(WasmtimeLibraryFilename)" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Target>
<ItemGroup>
<None Update="Modules/*.wat" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
</Project>

View File

@@ -1,3 +1,85 @@
# Using WebAssembly from .NET # Using WebAssembly from .NET
... more coming soon The [Wasmtime](https://www.nuget.org/packages/Wasmtime) NuGet package can be used to
programmatically interact with WebAssembly modules.
This guide will go over adding Wasmtime to your project and demonstrate a simple
example of using a WebAssembly module from C#.
Make sure you have a [.NET Core SDK 3.0 SDK or later](https://dotnet.microsoft.com/download)
installed before we get started!
## Getting started and simple example
Start by creating a new .NET Core console project:
```text
$ mkdir gcd
$ cd gcd
$ dotnet new console
```
Next, add a reference to the Wasmtime NuGet package to your project:
```text
$ dotnet add package --version 0.15.0-preview1 wasmtime
```
Copy this example WebAssembly text module into your project directory as `gcd.wat`.
```wat
{{#include ../examples/gcd.wat}}
```
This module exports a function for calculating the greatest common denominator of two numbers.
Replace the code in `Program.cs` with the following:
```c#
using System;
using Wasmtime;
namespace Tutorial
{
class Program
{
static void Main(string[] args)
{
using var host = new Host();
using var module = host.LoadModuleText("gcd.wat");
using dynamic instance = host.Instantiate(module);
Console.WriteLine($"gcd(27, 6) = {instance.gcd(27, 6)}");
}
}
}
```
Run the .NET core program:
```text
$ dotnet run
```
The program should output:
```text
gcd(27, 6) = 3
```
If this is the output you see, congrats! You've successfully ran your first
WebAssembly code in .NET!
## More examples and contributing
The [.NET embedding of Wasmtime repository](https://github.com/bytecodealliance/wasmtime-dotnet)
contains the source code for the Wasmtime NuGet package.
The repository also has more [examples](https://github.com/bytecodealliance/wasmtime-dotnet/tree/master/examples)
as well.
Feel free to browse those, but if you find anything missing don't
hesitate to [open an issue](https://github.com/bytecodealliance/wasmtime-dotnet/issues/new) and let us
know if you have any questions!