Move Wasmtime for .NET to the Wasmtime repo.
This moves the Wasmtime for .NET implementation to the Wasmtime repo. Wasmtime for .NET is a binding of the Wasmtime API for use in .NET.
This commit is contained in:
129
crates/misc/dotnet/src/Bindings/Binding.cs
Normal file
129
crates/misc/dotnet/src/Bindings/Binding.cs
Normal file
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using Wasmtime.Imports;
|
||||
|
||||
namespace Wasmtime.Bindings
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an abstract host binding.
|
||||
/// </summary>
|
||||
public abstract class Binding
|
||||
{
|
||||
internal abstract SafeHandle Bind(Store store, IHost host);
|
||||
|
||||
internal static void ThrowBindingException(Import import, MemberInfo member, string message)
|
||||
{
|
||||
throw new WasmtimeException($"Unable to bind '{member.DeclaringType.Name}.{member.Name}' to WebAssembly import '{import}': {message}.");
|
||||
}
|
||||
|
||||
internal static List<Binding> GetImportBindings(IHost host, Module module)
|
||||
{
|
||||
if (host is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(host));
|
||||
}
|
||||
|
||||
if (module is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(module));
|
||||
}
|
||||
|
||||
var flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly;
|
||||
var type = host.GetType();
|
||||
var methods = type.GetMethods(flags).Where(m => !m.IsSpecialName && Attribute.IsDefined(m, typeof(ImportAttribute)));
|
||||
var fields = type.GetFields(flags).Where(m => !m.IsSpecialName && Attribute.IsDefined(m, typeof(ImportAttribute)));
|
||||
|
||||
var bindings = new List<Binding>();
|
||||
foreach (var import in module.Imports.All)
|
||||
{
|
||||
switch (import)
|
||||
{
|
||||
case FunctionImport func:
|
||||
bindings.Add(BindFunction(func, methods));
|
||||
break;
|
||||
|
||||
case GlobalImport global:
|
||||
bindings.Add(BindGlobal(global, fields));
|
||||
break;
|
||||
|
||||
case MemoryImport memory:
|
||||
bindings.Add(BindMemory(memory, fields));
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("Unsupported import binding type.");
|
||||
}
|
||||
}
|
||||
|
||||
return bindings;
|
||||
}
|
||||
|
||||
private static FunctionBinding BindFunction(FunctionImport import, IEnumerable<MethodInfo> methods)
|
||||
{
|
||||
var method = methods.Where(m =>
|
||||
{
|
||||
var attribute = (ImportAttribute)m.GetCustomAttribute(typeof(ImportAttribute));
|
||||
if (attribute is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return attribute.Name == import.Name &&
|
||||
((string.IsNullOrEmpty(attribute.Module) &&
|
||||
string.IsNullOrEmpty(import.ModuleName)) ||
|
||||
attribute.Module == import.ModuleName);
|
||||
}
|
||||
).FirstOrDefault();
|
||||
|
||||
if (method is null)
|
||||
{
|
||||
throw new WasmtimeException($"Failed to bind function import '{import}': the host does not contain a method with a matching 'Import' attribute.");
|
||||
}
|
||||
|
||||
return new FunctionBinding(import, method);
|
||||
}
|
||||
|
||||
private static GlobalBinding BindGlobal(GlobalImport import, IEnumerable<FieldInfo> fields)
|
||||
{
|
||||
var field = fields.Where(f =>
|
||||
{
|
||||
var attribute = (ImportAttribute)f.GetCustomAttribute(typeof(ImportAttribute));
|
||||
return attribute.Name == import.Name &&
|
||||
((string.IsNullOrEmpty(attribute.Module) &&
|
||||
string.IsNullOrEmpty(import.ModuleName)) ||
|
||||
attribute.Module == import.ModuleName);
|
||||
}
|
||||
).FirstOrDefault();
|
||||
|
||||
if (field is null)
|
||||
{
|
||||
throw new WasmtimeException($"Failed to bind global import '{import}': the host does not contain a global field with a matching 'Import' attribute.");
|
||||
}
|
||||
|
||||
return new GlobalBinding(import, field);
|
||||
}
|
||||
|
||||
private static MemoryBinding BindMemory(MemoryImport import, IEnumerable<FieldInfo> fields)
|
||||
{
|
||||
var field = fields.Where(f =>
|
||||
{
|
||||
var attribute = (ImportAttribute)f.GetCustomAttribute(typeof(ImportAttribute));
|
||||
return attribute.Name == import.Name &&
|
||||
((string.IsNullOrEmpty(attribute.Module) &&
|
||||
string.IsNullOrEmpty(import.ModuleName)) ||
|
||||
attribute.Module == import.ModuleName);
|
||||
}
|
||||
).FirstOrDefault();
|
||||
|
||||
if (field is null)
|
||||
{
|
||||
throw new WasmtimeException($"Failed to bind memory import '{import}': the host does not contain a memory field with a matching 'Import' attribute.");
|
||||
}
|
||||
|
||||
return new MemoryBinding(import, field);
|
||||
}
|
||||
}
|
||||
}
|
||||
346
crates/misc/dotnet/src/Bindings/FunctionBinding.cs
Normal file
346
crates/misc/dotnet/src/Bindings/FunctionBinding.cs
Normal file
@@ -0,0 +1,346 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using Wasmtime.Imports;
|
||||
|
||||
namespace Wasmtime.Bindings
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a host function binding.
|
||||
/// </summary>
|
||||
public class FunctionBinding : Binding
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new function binding.
|
||||
/// </summary>
|
||||
/// <param name="import">The function import of the binding.</param>
|
||||
/// <param name="method">The method the import is bound to.</param>
|
||||
public FunctionBinding(FunctionImport import, MethodInfo method)
|
||||
{
|
||||
if (import is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(import));
|
||||
}
|
||||
|
||||
if (method is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(method));
|
||||
}
|
||||
|
||||
Import = import;
|
||||
Method = method;
|
||||
|
||||
Validate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The function import of the binding.
|
||||
/// </summary>
|
||||
public FunctionImport Import { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The method the import is bound to.
|
||||
/// </summary>
|
||||
public MethodInfo Method { get; private set; }
|
||||
|
||||
internal override SafeHandle Bind(Store store, IHost host)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
if (_callback != null)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot bind more than once.");
|
||||
}
|
||||
|
||||
_callback = CreateCallback(store, host);
|
||||
|
||||
var parameters = Interop.ToValueTypeVec(Import.Parameters);
|
||||
var results = Interop.ToValueTypeVec(Import.Results);
|
||||
using (var funcType = Interop.wasm_functype_new(ref parameters, ref results))
|
||||
{
|
||||
return Interop.wasm_func_new(store.Handle, funcType, _callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Validate()
|
||||
{
|
||||
if (Method.IsStatic)
|
||||
{
|
||||
ThrowBindingException(Import, Method, "method cannot be static");
|
||||
}
|
||||
|
||||
if (Method.IsGenericMethod)
|
||||
{
|
||||
ThrowBindingException(Import, Method, "method cannot be generic");
|
||||
}
|
||||
|
||||
if (Method.IsConstructor)
|
||||
{
|
||||
ThrowBindingException(Import, Method, "method cannot be a constructor");
|
||||
}
|
||||
|
||||
ValidateParameters();
|
||||
|
||||
ValidateReturnType();
|
||||
}
|
||||
|
||||
private void ValidateParameters()
|
||||
{
|
||||
var parameters = Method.GetParameters();
|
||||
if (parameters.Length != Import.Parameters.Count)
|
||||
{
|
||||
ThrowBindingException(
|
||||
Import,
|
||||
Method,
|
||||
$"parameter mismatch: import requires {Import.Parameters.Count} but the method has {parameters.Length}");
|
||||
}
|
||||
|
||||
for (int i = 0; i < parameters.Length; ++i)
|
||||
{
|
||||
var parameter = parameters[i];
|
||||
if (parameter.ParameterType.IsByRef)
|
||||
{
|
||||
if (parameter.IsOut)
|
||||
{
|
||||
ThrowBindingException(Import, Method, $"parameter '{parameter.Name}' cannot be an 'out' parameter");
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowBindingException(Import, Method, $"parameter '{parameter.Name}' cannot be a 'ref' parameter");
|
||||
}
|
||||
}
|
||||
|
||||
var expected = Import.Parameters[i];
|
||||
if (!Interop.TryGetValueKind(parameter.ParameterType, out var kind) || !Interop.IsMatchingKind(kind, expected))
|
||||
{
|
||||
ThrowBindingException(Import, Method, $"method parameter '{parameter.Name}' is expected to be of type '{Interop.ToString(expected)}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ValidateReturnType()
|
||||
{
|
||||
int resultsCount = Import.Results.Count();
|
||||
if (resultsCount == 0)
|
||||
{
|
||||
if (Method.ReturnType != typeof(void))
|
||||
{
|
||||
ThrowBindingException(Import, Method, "method must return void");
|
||||
}
|
||||
}
|
||||
else if (resultsCount == 1)
|
||||
{
|
||||
var expected = Import.Results[0];
|
||||
if (!Interop.TryGetValueKind(Method.ReturnType, out var kind) || !Interop.IsMatchingKind(kind, expected))
|
||||
{
|
||||
ThrowBindingException(Import, Method, $"return type is expected to be '{Interop.ToString(expected)}'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IsTupleOfSize(Method.ReturnType, resultsCount))
|
||||
{
|
||||
ThrowBindingException(Import, Method, $"return type is expected to be a tuple of size {resultsCount}");
|
||||
}
|
||||
|
||||
var typeArguments =
|
||||
Method.ReturnType.GetGenericArguments().SelectMany(type =>
|
||||
{
|
||||
if (type.IsConstructedGenericType)
|
||||
{
|
||||
return type.GenericTypeArguments;
|
||||
}
|
||||
return Enumerable.Repeat(type, 1);
|
||||
});
|
||||
|
||||
int i = 0;
|
||||
foreach (var typeArgument in typeArguments)
|
||||
{
|
||||
var expected = Import.Results[i];
|
||||
if (!Interop.TryGetValueKind(typeArgument, out var kind) || !Interop.IsMatchingKind(kind, expected))
|
||||
{
|
||||
ThrowBindingException(Import, Method, $"return tuple item #{i} is expected to be of type '{Interop.ToString(expected)}'");
|
||||
}
|
||||
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsTupleOfSize(Type type, int size)
|
||||
{
|
||||
if (!type.IsConstructedGenericType)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var definition = type.GetGenericTypeDefinition();
|
||||
|
||||
if (size == 0)
|
||||
{
|
||||
return definition == typeof(ValueTuple);
|
||||
}
|
||||
|
||||
if (size == 1)
|
||||
{
|
||||
return definition == typeof(ValueTuple<>);
|
||||
}
|
||||
|
||||
if (size == 2)
|
||||
{
|
||||
return definition == typeof(ValueTuple<,>);
|
||||
}
|
||||
|
||||
if (size == 3)
|
||||
{
|
||||
return definition == typeof(ValueTuple<,,>);
|
||||
}
|
||||
|
||||
if (size == 4)
|
||||
{
|
||||
return definition == typeof(ValueTuple<,,,>);
|
||||
}
|
||||
|
||||
if (size == 5)
|
||||
{
|
||||
return definition == typeof(ValueTuple<,,,,>);
|
||||
}
|
||||
|
||||
if (size == 6)
|
||||
{
|
||||
return definition == typeof(ValueTuple<,,,,,>);
|
||||
}
|
||||
|
||||
if (size == 7)
|
||||
{
|
||||
return definition == typeof(ValueTuple<,,,,,,>);
|
||||
}
|
||||
|
||||
if (definition != typeof(ValueTuple<,,,,,,,>))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return IsTupleOfSize(type.GetGenericArguments().Last(), size - 7);
|
||||
}
|
||||
|
||||
private unsafe Interop.WasmFuncCallback CreateCallback(Store store, IHost host)
|
||||
{
|
||||
var args = new object[Import.Parameters.Count];
|
||||
bool hasReturn = Method.ReturnType != typeof(void);
|
||||
var storeHandle = store.Handle;
|
||||
|
||||
Interop.WasmFuncCallback callback = (arguments, results) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
SetArgs(arguments, args);
|
||||
|
||||
var result = Method.Invoke(host, args);
|
||||
|
||||
if (hasReturn)
|
||||
{
|
||||
SetResults(result, results);
|
||||
}
|
||||
return IntPtr.Zero;
|
||||
}
|
||||
catch (TargetInvocationException ex)
|
||||
{
|
||||
var bytes = Encoding.UTF8.GetBytes(ex.InnerException.Message + "\0" /* exception messages need a null */);
|
||||
|
||||
fixed (byte* ptr = bytes)
|
||||
{
|
||||
Interop.wasm_byte_vec_t message = new Interop.wasm_byte_vec_t();
|
||||
message.size = (UIntPtr)bytes.Length;
|
||||
message.data = ptr;
|
||||
|
||||
return Interop.wasm_trap_new(storeHandle, ref message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return callback;
|
||||
}
|
||||
|
||||
private static unsafe void SetArgs(Interop.wasm_val_t* arguments, object[] args)
|
||||
{
|
||||
for (int i = 0; i < args.Length; ++i)
|
||||
{
|
||||
var arg = arguments[i];
|
||||
|
||||
switch (arg.kind)
|
||||
{
|
||||
case Interop.wasm_valkind_t.WASM_I32:
|
||||
args[i] = arg.of.i32;
|
||||
break;
|
||||
|
||||
case Interop.wasm_valkind_t.WASM_I64:
|
||||
args[i] = arg.of.i64;
|
||||
break;
|
||||
|
||||
case Interop.wasm_valkind_t.WASM_F32:
|
||||
args[i] = arg.of.f32;
|
||||
break;
|
||||
|
||||
case Interop.wasm_valkind_t.WASM_F64:
|
||||
args[i] = arg.of.f64;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("Unsupported value type.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe void SetResults(object value, Interop.wasm_val_t* results)
|
||||
{
|
||||
var tuple = value as ITuple;
|
||||
if (tuple is null)
|
||||
{
|
||||
SetResult(value, &results[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < tuple.Length; ++i)
|
||||
{
|
||||
SetResults(tuple[i], &results[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static unsafe void SetResult(object value, Interop.wasm_val_t* result)
|
||||
{
|
||||
switch (value)
|
||||
{
|
||||
case int i:
|
||||
result->kind = Interop.wasm_valkind_t.WASM_I32;
|
||||
result->of.i32 = i;
|
||||
break;
|
||||
|
||||
case long l:
|
||||
result->kind = Interop.wasm_valkind_t.WASM_I64;
|
||||
result->of.i64 = l;
|
||||
break;
|
||||
|
||||
case float f:
|
||||
result->kind = Interop.wasm_valkind_t.WASM_F32;
|
||||
result->of.f32 = f;
|
||||
break;
|
||||
|
||||
case double d:
|
||||
result->kind = Interop.wasm_valkind_t.WASM_F64;
|
||||
result->of.f64 = d;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("Unsupported return value type.");
|
||||
}
|
||||
}
|
||||
|
||||
private Interop.WasmFuncCallback _callback;
|
||||
}
|
||||
}
|
||||
125
crates/misc/dotnet/src/Bindings/GlobalBinding.cs
Normal file
125
crates/misc/dotnet/src/Bindings/GlobalBinding.cs
Normal file
@@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using Wasmtime.Imports;
|
||||
|
||||
namespace Wasmtime.Bindings
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a host global binding.
|
||||
/// </summary>
|
||||
public class GlobalBinding : Binding
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new global binding.
|
||||
/// </summary>
|
||||
/// <param name="import">The global import of the binding.</param>
|
||||
/// <param name="field">The field the import is bound to.</param>
|
||||
public GlobalBinding(GlobalImport import, FieldInfo field)
|
||||
{
|
||||
if (import is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(import));
|
||||
}
|
||||
|
||||
if (field is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(field));
|
||||
}
|
||||
|
||||
Import = import;
|
||||
Field = field;
|
||||
|
||||
Validate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The global import of the binding.
|
||||
/// </summary>
|
||||
public GlobalImport Import { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The field the import is bound to.
|
||||
/// </summary>
|
||||
public FieldInfo Field { get; private set; }
|
||||
|
||||
internal override SafeHandle Bind(Store store, IHost host)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
dynamic global = Field.GetValue(host);
|
||||
if (global.Handle != null)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot bind more than once.");
|
||||
}
|
||||
|
||||
var v = Interop.ToValue((object)global.InitialValue, Import.Kind);
|
||||
|
||||
var valueType = Interop.wasm_valtype_new(v.kind);
|
||||
var valueTypeHandle = valueType.DangerousGetHandle();
|
||||
valueType.SetHandleAsInvalid();
|
||||
|
||||
using (var globalType = Interop.wasm_globaltype_new(
|
||||
valueTypeHandle,
|
||||
Import.IsMutable ? Interop.wasm_mutability_t.WASM_VAR : Interop.wasm_mutability_t.WASM_CONST))
|
||||
{
|
||||
var handle = Interop.wasm_global_new(store.Handle, globalType, &v);
|
||||
global.Handle = handle;
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Validate()
|
||||
{
|
||||
if (Field.IsStatic)
|
||||
{
|
||||
ThrowBindingException(Import, Field, "field cannot be static");
|
||||
}
|
||||
|
||||
if (!Field.IsInitOnly)
|
||||
{
|
||||
ThrowBindingException(Import, Field, "field must be readonly");
|
||||
}
|
||||
|
||||
if (!Field.FieldType.IsGenericType)
|
||||
{
|
||||
ThrowBindingException(Import, Field, "field is expected to be of type 'Global<T>'");
|
||||
}
|
||||
|
||||
var definition = Field.FieldType.GetGenericTypeDefinition();
|
||||
if (definition == typeof(Global<>))
|
||||
{
|
||||
if (Import.IsMutable)
|
||||
{
|
||||
ThrowBindingException(Import, Field, "the import is mutable (use the 'MutableGlobal' type)");
|
||||
}
|
||||
}
|
||||
else if (definition == typeof(MutableGlobal<>))
|
||||
{
|
||||
if (!Import.IsMutable)
|
||||
{
|
||||
ThrowBindingException(Import, Field, "the import is constant (use the 'Global' type)");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowBindingException(Import, Field, "field is expected to be of type 'Global<T>' or 'MutableGlobal<T>'");
|
||||
}
|
||||
|
||||
var arg = Field.FieldType.GetGenericArguments()[0];
|
||||
|
||||
if (Interop.TryGetValueKind(arg, out var kind))
|
||||
{
|
||||
if (!Interop.IsMatchingKind(kind, Import.Kind))
|
||||
{
|
||||
ThrowBindingException(Import, Field, $"global type argument is expected to be of type '{Interop.ToString(Import.Kind)}'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ThrowBindingException(Import, Field, $"'{arg}' is not a valid global type");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
98
crates/misc/dotnet/src/Bindings/MemoryBinding.cs
Normal file
98
crates/misc/dotnet/src/Bindings/MemoryBinding.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using Wasmtime.Imports;
|
||||
|
||||
namespace Wasmtime.Bindings
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a host memory binding.
|
||||
/// </summary>
|
||||
public class MemoryBinding : Binding
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new memory binding.
|
||||
/// </summary>
|
||||
/// <param name="import">The memory import of the binding.</param>
|
||||
/// <param name="field">The field the import is bound to.</param>
|
||||
public MemoryBinding(MemoryImport import, FieldInfo field)
|
||||
{
|
||||
if (import is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(import));
|
||||
}
|
||||
|
||||
if (field is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(field));
|
||||
}
|
||||
|
||||
Import = import;
|
||||
Field = field;
|
||||
|
||||
Validate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The memory import of the binding.
|
||||
/// </summary>
|
||||
public MemoryImport Import { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The field the import is bound to.
|
||||
/// </summary>
|
||||
public FieldInfo Field { get; private set; }
|
||||
|
||||
internal override SafeHandle Bind(Store store, IHost host)
|
||||
{
|
||||
dynamic memory = Field.GetValue(host);
|
||||
if (memory.Handle != null)
|
||||
{
|
||||
throw new InvalidOperationException("Cannot bind more than once.");
|
||||
}
|
||||
|
||||
uint min = memory.Minimum;
|
||||
uint max = memory.Maximum;
|
||||
|
||||
if (min != Import.Minimum)
|
||||
{
|
||||
ThrowBindingException(Import, Field, $"Memory does not have the expected minimum of {Import.Minimum} page(s)");
|
||||
}
|
||||
if (max != Import.Maximum)
|
||||
{
|
||||
ThrowBindingException(Import, Field, $"Memory does not have the expected maximum of {Import.Maximum} page(s)");
|
||||
}
|
||||
|
||||
unsafe
|
||||
{
|
||||
Interop.wasm_limits_t limits = new Interop.wasm_limits_t();
|
||||
limits.min = min;
|
||||
limits.max = max;
|
||||
using (var memoryType = Interop.wasm_memorytype_new(&limits))
|
||||
{
|
||||
var handle = Interop.wasm_memory_new(store.Handle, memoryType);
|
||||
memory.Handle = handle;
|
||||
return handle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Validate()
|
||||
{
|
||||
if (Field.IsStatic)
|
||||
{
|
||||
ThrowBindingException(Import, Field, "field cannot be static");
|
||||
}
|
||||
|
||||
if (!Field.IsInitOnly)
|
||||
{
|
||||
ThrowBindingException(Import, Field, "field must be readonly");
|
||||
}
|
||||
|
||||
if (Field.FieldType != typeof(Memory))
|
||||
{
|
||||
ThrowBindingException(Import, Field, "field is expected to be of type 'Memory'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
44
crates/misc/dotnet/src/Engine.cs
Normal file
44
crates/misc/dotnet/src/Engine.cs
Normal file
@@ -0,0 +1,44 @@
|
||||
using System;
|
||||
|
||||
namespace Wasmtime
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the Wasmtime engine.
|
||||
/// </summary>
|
||||
public class Engine : IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="Engine" />.
|
||||
/// </summary>
|
||||
public Engine()
|
||||
{
|
||||
Handle = Interop.wasm_engine_new();
|
||||
|
||||
if (Handle.IsInvalid)
|
||||
{
|
||||
throw new WasmtimeException("Failed to create Wasmtime engine.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new Wasmtime <see cref="Store" />.
|
||||
/// </summary>
|
||||
/// <returns>Returns the new <see cref="Store" />.</returns>
|
||||
public Store CreateStore()
|
||||
{
|
||||
return new Store(this);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
if (!Handle.IsInvalid)
|
||||
{
|
||||
Handle.Dispose();
|
||||
Handle.SetHandleAsInvalid();
|
||||
}
|
||||
}
|
||||
|
||||
internal Interop.EngineHandle Handle { get; private set; }
|
||||
}
|
||||
}
|
||||
31
crates/misc/dotnet/src/Exports/Export.cs
Normal file
31
crates/misc/dotnet/src/Exports/Export.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
97
crates/misc/dotnet/src/Exports/Exports.cs
Normal file
97
crates/misc/dotnet/src/Exports/Exports.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
34
crates/misc/dotnet/src/Exports/FunctionExport.cs
Normal file
34
crates/misc/dotnet/src/Exports/FunctionExport.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
30
crates/misc/dotnet/src/Exports/GlobalExport.cs
Normal file
30
crates/misc/dotnet/src/Exports/GlobalExport.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
35
crates/misc/dotnet/src/Exports/MemoryExport.cs
Normal file
35
crates/misc/dotnet/src/Exports/MemoryExport.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
42
crates/misc/dotnet/src/Exports/TableExport.cs
Normal file
42
crates/misc/dotnet/src/Exports/TableExport.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
87
crates/misc/dotnet/src/Externs/ExternFunction.cs
Normal file
87
crates/misc/dotnet/src/Externs/ExternFunction.cs
Normal file
@@ -0,0 +1,87 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
62
crates/misc/dotnet/src/Externs/ExternGlobal.cs
Normal file
62
crates/misc/dotnet/src/Externs/ExternGlobal.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
245
crates/misc/dotnet/src/Externs/ExternMemory.cs
Normal file
245
crates/misc/dotnet/src/Externs/ExternMemory.cs
Normal file
@@ -0,0 +1,245 @@
|
||||
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
|
||||
{
|
||||
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;
|
||||
|
||||
/// <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(_memory);
|
||||
var size = Convert.ToInt32(Interop.wasm_memory_data_size(_memory).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>
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
||||
private MemoryExport _export;
|
||||
private IntPtr _memory;
|
||||
}
|
||||
}
|
||||
67
crates/misc/dotnet/src/Externs/Externs.cs
Normal file
67
crates/misc/dotnet/src/Externs/Externs.cs
Normal file
@@ -0,0 +1,67 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
50
crates/misc/dotnet/src/Global.cs
Normal file
50
crates/misc/dotnet/src/Global.cs
Normal file
@@ -0,0 +1,50 @@
|
||||
using System;
|
||||
|
||||
namespace Wasmtime
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a constant WebAssembly global value.
|
||||
/// </summary>
|
||||
public class Global<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="Global<T>" /> with the given initial value.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">The initial value of the global.</param>
|
||||
public Global(T initialValue)
|
||||
{
|
||||
InitialValue = initialValue;
|
||||
Kind = Interop.ToValueKind(typeof(T));
|
||||
}
|
||||
|
||||
/// <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);
|
||||
|
||||
// TODO: figure out a way that doesn't box the value
|
||||
return (T)Interop.ToObject(v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal ValueKind Kind { get; private set; }
|
||||
|
||||
internal Interop.GlobalHandle Handle { get; set; }
|
||||
|
||||
internal T InitialValue { get; private set; }
|
||||
}
|
||||
}
|
||||
25
crates/misc/dotnet/src/IHost.cs
Normal file
25
crates/misc/dotnet/src/IHost.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Wasmtime.Bindings;
|
||||
|
||||
namespace Wasmtime
|
||||
{
|
||||
/// <summary>
|
||||
/// The interface implemented by Wasmtime hosts.
|
||||
/// </summary>
|
||||
public interface IHost
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="Wasmtime.Instance" /> that the host is bound to.
|
||||
/// </summary>
|
||||
/// <remarks>A host can only bind to one module instance at a time.</remarks>
|
||||
Instance Instance { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the import bindings of the host given a WebAssembly module.
|
||||
/// </summary>
|
||||
/// <param name="module">The WebAssembly module to get the import bindings for.</param>
|
||||
/// <returns>Returns the list of import bindings for the host.</returns>
|
||||
List<Binding> GetImportBindings(Module module) => Binding.GetImportBindings(this, module);
|
||||
}
|
||||
}
|
||||
31
crates/misc/dotnet/src/ImportAttribute.cs
Normal file
31
crates/misc/dotnet/src/ImportAttribute.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
|
||||
namespace Wasmtime
|
||||
{
|
||||
/// <summary>
|
||||
/// Used to mark .NET methods and fields as imports to a WebAssembly module.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Field)]
|
||||
public class ImportAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Constructs a new <see cref="ImportAttribute"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the import.</param>
|
||||
public ImportAttribute(string name)
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The name of the import.
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The module name of the import.
|
||||
/// </summary>
|
||||
/// <remarks>A null or empty module name implies that the import is not scoped to a module.</remarks>
|
||||
public string Module { get; set; }
|
||||
}
|
||||
}
|
||||
34
crates/misc/dotnet/src/Imports/FunctionImport.cs
Normal file
34
crates/misc/dotnet/src/Imports/FunctionImport.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
30
crates/misc/dotnet/src/Imports/GlobalImport.cs
Normal file
30
crates/misc/dotnet/src/Imports/GlobalImport.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
39
crates/misc/dotnet/src/Imports/Import.cs
Normal file
39
crates/misc/dotnet/src/Imports/Import.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
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
|
||||
{
|
||||
var moduleName = Interop.wasm_importtype_module(importType);
|
||||
ModuleName = Marshal.PtrToStringUTF8((IntPtr)moduleName->data, (int)moduleName->size);
|
||||
|
||||
var name = Interop.wasm_importtype_name(importType);
|
||||
Name = Marshal.PtrToStringUTF8((IntPtr)name->data, (int)name->size);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string ToString()
|
||||
{
|
||||
return $"{ModuleName}{(string.IsNullOrEmpty(ModuleName) ? "" : ".")}{Name}";
|
||||
}
|
||||
}
|
||||
}
|
||||
97
crates/misc/dotnet/src/Imports/Imports.cs
Normal file
97
crates/misc/dotnet/src/Imports/Imports.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
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
|
||||
{
|
||||
internal Imports(Module module)
|
||||
{
|
||||
Interop.wasm_importtype_vec_t imports;
|
||||
Interop.wasm_module_imports(module.Handle, out imports);
|
||||
|
||||
try
|
||||
{
|
||||
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;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Interop.wasm_importtype_vec_delete(ref imports);
|
||||
}
|
||||
}
|
||||
|
||||
/// <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; }
|
||||
}
|
||||
}
|
||||
35
crates/misc/dotnet/src/Imports/MemoryImport.cs
Normal file
35
crates/misc/dotnet/src/Imports/MemoryImport.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
42
crates/misc/dotnet/src/Imports/TableImport.cs
Normal file
42
crates/misc/dotnet/src/Imports/TableImport.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
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; }
|
||||
}
|
||||
}
|
||||
145
crates/misc/dotnet/src/Instance.cs
Normal file
145
crates/misc/dotnet/src/Instance.cs
Normal file
@@ -0,0 +1,145 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Dynamic;
|
||||
using Wasmtime.Externs;
|
||||
|
||||
namespace Wasmtime
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents an instantiated WebAssembly module.
|
||||
/// </summary>
|
||||
public class Instance : DynamicObject, IDisposable
|
||||
{
|
||||
internal Instance(Module module, IHost host)
|
||||
{
|
||||
Host = host;
|
||||
Module = module;
|
||||
|
||||
var bindings = host.GetImportBindings(module);
|
||||
var handles = bindings.Select(b => b.Bind(module.Store, host)).ToList();
|
||||
|
||||
unsafe
|
||||
{
|
||||
Handle = Interop.wasm_instance_new(
|
||||
Module.Store.Handle,
|
||||
Module.Handle,
|
||||
handles.Select(h => ToExtern(h)).ToArray(),
|
||||
out var trap);
|
||||
|
||||
if (trap != IntPtr.Zero)
|
||||
{
|
||||
throw TrapException.FromOwnedTrap(trap);
|
||||
}
|
||||
}
|
||||
|
||||
if (Handle.IsInvalid)
|
||||
{
|
||||
throw new WasmtimeException($"Failed to instantiate module '{module.Name}'.");
|
||||
}
|
||||
|
||||
// Dispose of all function handles (not needed at runtime)
|
||||
foreach (var h in handles.Where(h => h is Interop.FunctionHandle))
|
||||
{
|
||||
h.Dispose();
|
||||
}
|
||||
|
||||
Interop.wasm_instance_exports(Handle, out _externs);
|
||||
|
||||
Externs = new Wasmtime.Externs.Externs(Module.Exports, _externs);
|
||||
|
||||
_functions = Externs.Functions.ToDictionary(f => f.Name);
|
||||
_globals = Externs.Globals.ToDictionary(g => g.Name);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The host associated with this instance.
|
||||
/// </summary>
|
||||
public IHost Host { get; private set; }
|
||||
|
||||
/// <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 void Dispose()
|
||||
{
|
||||
if (!Handle.IsInvalid)
|
||||
{
|
||||
Handle.Dispose();
|
||||
Handle.SetHandleAsInvalid();
|
||||
}
|
||||
if (_externs.size != UIntPtr.Zero)
|
||||
{
|
||||
Interop.wasm_extern_vec_delete(ref _externs);
|
||||
_externs.size = UIntPtr.Zero;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
private static unsafe IntPtr ToExtern(SafeHandle handle)
|
||||
{
|
||||
switch (handle)
|
||||
{
|
||||
case Interop.FunctionHandle f:
|
||||
return Interop.wasm_func_as_extern(f);
|
||||
|
||||
case Interop.GlobalHandle g:
|
||||
return Interop.wasm_global_as_extern(g);
|
||||
|
||||
case Interop.MemoryHandle m:
|
||||
return Interop.wasm_memory_as_extern(m);
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("Unexpected handle type.");
|
||||
}
|
||||
}
|
||||
|
||||
internal Interop.InstanceHandle Handle { get; private set; }
|
||||
private Interop.wasm_extern_vec_t _externs;
|
||||
private Dictionary<string, ExternFunction> _functions;
|
||||
private Dictionary<string, ExternGlobal> _globals;
|
||||
}
|
||||
}
|
||||
762
crates/misc/dotnet/src/Interop.cs
Normal file
762
crates/misc/dotnet/src/Interop.cs
Normal file
@@ -0,0 +1,762 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Wasmtime
|
||||
{
|
||||
/// <summary>
|
||||
/// Implements the Wasmtime API bindings.
|
||||
/// </summary>
|
||||
/// <remarks>See https://github.com/WebAssembly/wasm-c-api/blob/master/include/wasm.h for the C API reference.</remarks>
|
||||
internal static class Interop
|
||||
{
|
||||
internal class EngineHandle : SafeHandle
|
||||
{
|
||||
public EngineHandle() : base(IntPtr.Zero, true)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Interop.wasm_engine_delete(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class StoreHandle : SafeHandle
|
||||
{
|
||||
public StoreHandle() : base(IntPtr.Zero, true)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Interop.wasm_store_delete(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ModuleHandle : SafeHandle
|
||||
{
|
||||
public ModuleHandle() : base(IntPtr.Zero, true)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Interop.wasm_module_delete(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class FunctionHandle : SafeHandle
|
||||
{
|
||||
public FunctionHandle() : base(IntPtr.Zero, true)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Interop.wasm_func_delete(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class GlobalHandle : SafeHandle
|
||||
{
|
||||
public GlobalHandle() : base(IntPtr.Zero, true)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Interop.wasm_global_delete(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class MemoryHandle : SafeHandle
|
||||
{
|
||||
public MemoryHandle() : base(IntPtr.Zero, true)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Interop.wasm_memory_delete(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class InstanceHandle : SafeHandle
|
||||
{
|
||||
public InstanceHandle() : base(IntPtr.Zero, true)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Interop.wasm_instance_delete(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class FuncTypeHandle : SafeHandle
|
||||
{
|
||||
public FuncTypeHandle() : base(IntPtr.Zero, true)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Interop.wasm_functype_delete(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class GlobalTypeHandle : SafeHandle
|
||||
{
|
||||
public GlobalTypeHandle() : base(IntPtr.Zero, true)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Interop.wasm_globaltype_delete(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class MemoryTypeHandle : SafeHandle
|
||||
{
|
||||
public MemoryTypeHandle() : base(IntPtr.Zero, true)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Interop.wasm_memorytype_delete(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
internal class ValueTypeHandle : SafeHandle
|
||||
{
|
||||
public ValueTypeHandle() : base(IntPtr.Zero, true)
|
||||
{
|
||||
}
|
||||
|
||||
public override bool IsInvalid => handle == IntPtr.Zero;
|
||||
|
||||
protected override bool ReleaseHandle()
|
||||
{
|
||||
Interop.wasm_valtype_delete(handle);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct wasm_byte_vec_t
|
||||
{
|
||||
public UIntPtr size;
|
||||
public byte* data;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct wasm_valtype_vec_t
|
||||
{
|
||||
public UIntPtr size;
|
||||
public IntPtr* data;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct wasm_export_vec_t
|
||||
{
|
||||
public UIntPtr size;
|
||||
public IntPtr* data;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct wasm_extern_vec_t
|
||||
{
|
||||
public UIntPtr size;
|
||||
public IntPtr* data;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct wasm_importtype_vec_t
|
||||
{
|
||||
public UIntPtr size;
|
||||
public IntPtr* data;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal unsafe struct wasm_exporttype_vec_t
|
||||
{
|
||||
public UIntPtr size;
|
||||
public IntPtr* data;
|
||||
}
|
||||
|
||||
internal enum wasm_valkind_t : byte
|
||||
{
|
||||
WASM_I32,
|
||||
WASM_I64,
|
||||
WASM_F32,
|
||||
WASM_F64,
|
||||
WASM_ANYREF = 128,
|
||||
WASM_FUNCREF,
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
internal struct wasm_val_union_t
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public int i32;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public long i64;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public float f32;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public double f64;
|
||||
|
||||
[FieldOffset(0)]
|
||||
public IntPtr reference;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct wasm_val_t
|
||||
{
|
||||
public wasm_valkind_t kind;
|
||||
public wasm_val_union_t of;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct wasm_limits_t
|
||||
{
|
||||
public uint min;
|
||||
|
||||
public uint max;
|
||||
}
|
||||
|
||||
public static wasm_val_t ToValue(object o, ValueKind kind)
|
||||
{
|
||||
wasm_val_t value = new wasm_val_t();
|
||||
switch (kind)
|
||||
{
|
||||
case ValueKind.Int32:
|
||||
value.kind = wasm_valkind_t.WASM_I32;
|
||||
value.of.i32 = (int)Convert.ChangeType(o, TypeCode.Int32);
|
||||
break;
|
||||
|
||||
case ValueKind.Int64:
|
||||
value.kind = wasm_valkind_t.WASM_I64;
|
||||
value.of.i64 = (long)Convert.ChangeType(o, TypeCode.Int64);
|
||||
break;
|
||||
|
||||
case ValueKind.Float32:
|
||||
value.kind = wasm_valkind_t.WASM_F32;
|
||||
value.of.f32 = (float)Convert.ChangeType(o, TypeCode.Single);
|
||||
break;
|
||||
|
||||
case ValueKind.Float64:
|
||||
value.kind = wasm_valkind_t.WASM_F64;
|
||||
value.of.f64 = (double)Convert.ChangeType(o, TypeCode.Double);
|
||||
break;
|
||||
|
||||
// TODO: support AnyRef
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("Unsupported value type.");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public static unsafe object ToObject(wasm_val_t* v)
|
||||
{
|
||||
switch (v->kind)
|
||||
{
|
||||
case Interop.wasm_valkind_t.WASM_I32:
|
||||
return v->of.i32;
|
||||
|
||||
case Interop.wasm_valkind_t.WASM_I64:
|
||||
return v->of.i64;
|
||||
|
||||
case Interop.wasm_valkind_t.WASM_F32:
|
||||
return v->of.f32;
|
||||
|
||||
case Interop.wasm_valkind_t.WASM_F64:
|
||||
return v->of.f64;
|
||||
|
||||
// TODO: support AnyRef
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("Unsupported value kind.");
|
||||
}
|
||||
}
|
||||
|
||||
public static bool TryGetValueKind(Type type, out ValueKind kind)
|
||||
{
|
||||
if (type == typeof(int))
|
||||
{
|
||||
kind = ValueKind.Int32;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == typeof(long))
|
||||
{
|
||||
kind = ValueKind.Int64;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == typeof(float))
|
||||
{
|
||||
kind = ValueKind.Float32;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type == typeof(double))
|
||||
{
|
||||
kind = ValueKind.Float64;
|
||||
return true;
|
||||
}
|
||||
|
||||
kind = default(ValueKind);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static ValueKind ToValueKind(Type type)
|
||||
{
|
||||
if (TryGetValueKind(type, out var kind))
|
||||
{
|
||||
return kind;
|
||||
}
|
||||
|
||||
throw new NotSupportedException($"Type '{type}' is not a supported WebAssembly value type.");
|
||||
}
|
||||
|
||||
public static string ToString(ValueKind kind)
|
||||
{
|
||||
switch (kind)
|
||||
{
|
||||
case ValueKind.Int32:
|
||||
return "int";
|
||||
|
||||
case ValueKind.Int64:
|
||||
return "long";
|
||||
|
||||
case ValueKind.Float32:
|
||||
return "float";
|
||||
|
||||
case ValueKind.Float64:
|
||||
return "double";
|
||||
|
||||
default:
|
||||
throw new NotSupportedException("Unsupported value kind.");
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsMatchingKind(ValueKind kind, ValueKind expected)
|
||||
{
|
||||
if (kind == expected)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (expected == ValueKind.AnyRef)
|
||||
{
|
||||
return kind == ValueKind.FuncRef;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal unsafe delegate IntPtr WasmFuncCallback(wasm_val_t* parameters, wasm_val_t* results);
|
||||
|
||||
internal enum wasm_externkind_t : byte
|
||||
{
|
||||
WASM_EXTERN_FUNC,
|
||||
WASM_EXTERN_GLOBAL,
|
||||
WASM_EXTERN_TABLE,
|
||||
WASM_EXTERN_MEMORY,
|
||||
}
|
||||
|
||||
internal enum wasm_mutability_t : byte
|
||||
{
|
||||
WASM_CONST,
|
||||
WASM_VAR,
|
||||
}
|
||||
|
||||
internal static unsafe List<ValueKind> ToValueKindList(Interop.wasm_valtype_vec_t* vec)
|
||||
{
|
||||
var list = new List<ValueKind>((int)vec->size);
|
||||
|
||||
for (int i = 0; i < (int)vec->size; ++i)
|
||||
{
|
||||
list.Add(Interop.wasm_valtype_kind(vec->data[i]));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
internal static Interop.wasm_valtype_vec_t ToValueTypeVec(IReadOnlyList<ValueKind> collection)
|
||||
{
|
||||
Interop.wasm_valtype_vec_t vec;
|
||||
Interop.wasm_valtype_vec_new_uninitialized(out vec, (UIntPtr)collection.Count);
|
||||
|
||||
int i = 0;
|
||||
foreach (var type in collection)
|
||||
{
|
||||
var valType = Interop.wasm_valtype_new((wasm_valkind_t)type);
|
||||
unsafe
|
||||
{
|
||||
vec.data[i++] = valType.DangerousGetHandle();
|
||||
}
|
||||
valType.SetHandleAsInvalid();
|
||||
}
|
||||
|
||||
return vec;
|
||||
}
|
||||
|
||||
// Engine imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern EngineHandle wasm_engine_new();
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_engine_delete(IntPtr engine);
|
||||
|
||||
// Store imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern StoreHandle wasm_store_new(EngineHandle engine);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_store_delete(IntPtr engine);
|
||||
|
||||
// Byte vec imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_byte_vec_new_empty(out wasm_byte_vec_t vec);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_byte_vec_new_uninitialized(out wasm_byte_vec_t vec, UIntPtr length);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_byte_vec_new(out wasm_byte_vec_t vec, UIntPtr length, byte[] data);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_byte_vec_copy(out wasm_byte_vec_t vec, ref wasm_byte_vec_t src);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_byte_vec_delete(ref wasm_byte_vec_t vec);
|
||||
|
||||
// Value type vec imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_valtype_vec_new_empty(out wasm_valtype_vec_t vec);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_valtype_vec_new_uninitialized(out wasm_valtype_vec_t vec, UIntPtr length);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_valtype_vec_new(out wasm_valtype_vec_t vec, UIntPtr length, IntPtr[] data);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_valtype_vec_copy(out wasm_valtype_vec_t vec, ref wasm_valtype_vec_t src);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_valtype_vec_delete(ref wasm_valtype_vec_t vec);
|
||||
|
||||
// Extern vec imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_extern_vec_new_empty(out wasm_extern_vec_t vec);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_extern_vec_new_uninitialized(out wasm_extern_vec_t vec, UIntPtr length);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_extern_vec_new(out wasm_extern_vec_t vec, UIntPtr length, IntPtr[] data);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_extern_vec_copy(out wasm_extern_vec_t vec, ref wasm_extern_vec_t src);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_extern_vec_delete(ref wasm_extern_vec_t vec);
|
||||
|
||||
// Import type vec imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_importtype_vec_new_empty(out wasm_importtype_vec_t vec);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_importtype_vec_new_uninitialized(out wasm_importtype_vec_t vec, UIntPtr length);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_importtype_vec_new(out wasm_importtype_vec_t vec, UIntPtr length, IntPtr[] data);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_importtype_vec_copy(out wasm_importtype_vec_t vec, ref wasm_importtype_vec_t src);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_importtype_vec_delete(ref wasm_importtype_vec_t vec);
|
||||
|
||||
// Export type vec imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_exporttype_vec_new_empty(out wasm_exporttype_vec_t vec);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_exporttype_vec_new_uninitialized(out wasm_exporttype_vec_t vec, UIntPtr length);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_exporttype_vec_new(out wasm_exporttype_vec_t vec, UIntPtr length, IntPtr[] data);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_exporttype_vec_copy(out wasm_exporttype_vec_t vec, ref wasm_exporttype_vec_t src);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_exporttype_vec_delete(ref wasm_exporttype_vec_t vec);
|
||||
|
||||
// Import type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern unsafe wasm_byte_vec_t* wasm_importtype_module(IntPtr importType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern unsafe wasm_byte_vec_t* wasm_importtype_name(IntPtr importType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern unsafe IntPtr wasm_importtype_type(IntPtr importType);
|
||||
|
||||
// Export type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern unsafe wasm_byte_vec_t* wasm_exporttype_name(IntPtr exportType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern unsafe IntPtr wasm_exporttype_type(IntPtr exportType);
|
||||
|
||||
// Module imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern ModuleHandle wasm_module_new(StoreHandle store, ref wasm_byte_vec_t bytes);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_module_imports(ModuleHandle module, out wasm_importtype_vec_t imports);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_module_exports(ModuleHandle module, out wasm_exporttype_vec_t exports);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_module_delete(IntPtr module);
|
||||
|
||||
// Value type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern ValueTypeHandle wasm_valtype_new(wasm_valkind_t kind);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_valtype_delete(IntPtr valueType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern ValueKind wasm_valtype_kind(IntPtr valueType);
|
||||
|
||||
// Extern imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern wasm_externkind_t wasm_extern_kind(IntPtr ext);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_extern_type(IntPtr ext);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_extern_as_func(IntPtr ext);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_extern_as_global(IntPtr ext);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_extern_as_table(IntPtr ext);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_extern_as_memory(IntPtr ext);
|
||||
|
||||
// Extern type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern wasm_externkind_t wasm_externtype_kind(IntPtr externType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_externtype_as_functype_const(IntPtr externType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_externtype_as_globaltype_const(IntPtr externType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_externtype_as_tabletype_const(IntPtr externType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_externtype_as_memorytype_const(IntPtr externType);
|
||||
|
||||
// Function imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern FunctionHandle wasm_func_new(StoreHandle store, FuncTypeHandle type, WasmFuncCallback callback);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_func_delete(IntPtr function);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static unsafe extern IntPtr wasm_func_call(IntPtr function, wasm_val_t* args, wasm_val_t* results);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_func_as_extern(FunctionHandle function);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_global_as_extern(GlobalHandle global);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_memory_as_extern(MemoryHandle memory);
|
||||
|
||||
// Function type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern unsafe wasm_valtype_vec_t* wasm_functype_params(IntPtr funcType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern unsafe wasm_valtype_vec_t* wasm_functype_results(IntPtr funcType);
|
||||
|
||||
// Instance imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern unsafe InstanceHandle wasm_instance_new(StoreHandle store, ModuleHandle module, IntPtr[] imports, out IntPtr trap);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_instance_delete(IntPtr ext);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_instance_exports(InstanceHandle instance, out wasm_extern_vec_t exports);
|
||||
|
||||
// Function type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern FuncTypeHandle wasm_functype_new(ref wasm_valtype_vec_t parameters, ref wasm_valtype_vec_t results);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_functype_delete(IntPtr functype);
|
||||
|
||||
// Global type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern GlobalTypeHandle wasm_globaltype_new(IntPtr valueType, wasm_mutability_t mutability);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_globaltype_delete(IntPtr globalType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_globaltype_content(IntPtr globalType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern wasm_mutability_t wasm_globaltype_mutability(IntPtr globalType);
|
||||
|
||||
// Memory type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern unsafe MemoryTypeHandle wasm_memorytype_new(wasm_limits_t* limits);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_memorytype_delete(IntPtr memoryType);
|
||||
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern unsafe wasm_limits_t* wasm_memorytype_limits(MemoryTypeHandle memoryType);
|
||||
|
||||
// Trap imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_trap_new(StoreHandle store, ref wasm_byte_vec_t message);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_trap_delete(IntPtr trap);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_trap_message(IntPtr trap, out wasm_byte_vec_t message);
|
||||
|
||||
// Table type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_tabletype_element(IntPtr tableType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static unsafe extern wasm_limits_t* wasm_tabletype_limits(IntPtr tableType);
|
||||
|
||||
// Memory type imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static unsafe extern wasm_limits_t* wasm_memorytype_limits(IntPtr memoryType);
|
||||
|
||||
// Global imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static unsafe extern GlobalHandle wasm_global_new(StoreHandle handle, GlobalTypeHandle globalType, wasm_val_t* initialValue);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_global_delete(IntPtr global);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_global_type(IntPtr global);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static unsafe extern void wasm_global_get(IntPtr global, wasm_val_t* value);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static unsafe extern void wasm_global_set(IntPtr global, wasm_val_t* value);
|
||||
|
||||
// Memory imports
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern MemoryHandle wasm_memory_new(StoreHandle handle, MemoryTypeHandle memoryType);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern void wasm_memory_delete(IntPtr memory);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern IntPtr wasm_memory_type(MemoryHandle memory);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static unsafe extern byte* wasm_memory_data(IntPtr memory);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern UIntPtr wasm_memory_data_size(IntPtr memory);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern uint wasm_memory_size(MemoryHandle memory);
|
||||
|
||||
[DllImport("wasmtime")]
|
||||
public static extern bool wasm_memory_grow(MemoryHandle memory, uint delta);
|
||||
}
|
||||
}
|
||||
270
crates/misc/dotnet/src/Memory.cs
Normal file
270
crates/misc/dotnet/src/Memory.cs
Normal file
@@ -0,0 +1,270 @@
|
||||
using System;
|
||||
using System.Text;
|
||||
using System.Buffers.Binary;
|
||||
|
||||
namespace Wasmtime
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a WebAssembly memory.
|
||||
/// </summary>
|
||||
public class Memory
|
||||
{
|
||||
/// <summary>
|
||||
/// The size, in bytes, of a WebAssembly memory page.
|
||||
/// </summary>
|
||||
public const int PageSize = 65536;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new memory with the given minimum and maximum page counts.
|
||||
/// </summary>
|
||||
/// <param name="minimum"></param>
|
||||
/// <param name="maximum"></param>
|
||||
public Memory(uint minimum = 1, uint maximum = uint.MaxValue)
|
||||
{
|
||||
if (minimum == 0)
|
||||
{
|
||||
throw new ArgumentException("The minimum cannot be zero..", nameof(minimum));
|
||||
}
|
||||
|
||||
if (maximum < minimum)
|
||||
{
|
||||
throw new ArgumentException("The maximum cannot be less than the minimum.", nameof(maximum));
|
||||
}
|
||||
|
||||
Minimum = minimum;
|
||||
Maximum = maximum;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The minimum memory size (in WebAssembly page units).
|
||||
/// </summary>
|
||||
public uint Minimum { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The minimum memory size (in WebAssembly page units).
|
||||
/// </summary>
|
||||
public uint Maximum { get; private set; }
|
||||
|
||||
/// <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(_handle.DangerousGetHandle());
|
||||
var size = Convert.ToInt32(Interop.wasm_memory_data_size(_handle.DangerousGetHandle()).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>
|
||||
/// 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);
|
||||
}
|
||||
}
|
||||
|
||||
internal Interop.MemoryHandle Handle
|
||||
{
|
||||
get
|
||||
{
|
||||
return _handle;
|
||||
}
|
||||
set
|
||||
{
|
||||
_handle = value;
|
||||
}
|
||||
}
|
||||
|
||||
private Interop.MemoryHandle _handle;
|
||||
}
|
||||
}
|
||||
101
crates/misc/dotnet/src/Module.cs
Normal file
101
crates/misc/dotnet/src/Module.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Wasmtime
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a WebAssembly module.
|
||||
/// </summary>
|
||||
public class Module : IDisposable
|
||||
{
|
||||
internal Module(Store store, string name, byte[] bytes)
|
||||
{
|
||||
if (store.Handle.IsInvalid)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(store));
|
||||
}
|
||||
|
||||
var bytesHandle = GCHandle.Alloc(bytes, GCHandleType.Pinned);
|
||||
|
||||
try
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
Interop.wasm_byte_vec_t vec;
|
||||
vec.size = (UIntPtr)bytes.Length;
|
||||
vec.data = (byte*)bytesHandle.AddrOfPinnedObject();
|
||||
|
||||
Handle = Interop.wasm_module_new(store.Handle, ref vec);
|
||||
}
|
||||
|
||||
if (Handle.IsInvalid)
|
||||
{
|
||||
throw new WasmtimeException($"WebAssembly module '{name}' is not valid.");
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
bytesHandle.Free();
|
||||
}
|
||||
|
||||
Store = store;
|
||||
Name = name;
|
||||
Imports = new Wasmtime.Imports.Imports(this);
|
||||
Exports = new Wasmtime.Exports.Exports(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a WebAssembly module for the given host.
|
||||
/// </summary>
|
||||
/// <param name="host">The host to use for the WebAssembly module's instance.</param>
|
||||
/// <returns>Returns a new <see cref="Instance" />.</returns>
|
||||
public Instance Instantiate(IHost host)
|
||||
{
|
||||
if (host is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(host));
|
||||
}
|
||||
|
||||
if (host.Instance != null)
|
||||
{
|
||||
throw new InvalidOperationException("The host has already been associated with an instantiated module.");
|
||||
}
|
||||
|
||||
host.Instance = new Instance(this, host);
|
||||
return host.Instance;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Store"/> associated with the module.
|
||||
/// </summary>
|
||||
public Store Store { get; private set; }
|
||||
|
||||
/// <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();
|
||||
}
|
||||
}
|
||||
|
||||
internal Interop.ModuleHandle Handle { get; private set; }
|
||||
}
|
||||
}
|
||||
65
crates/misc/dotnet/src/MutableGlobal.cs
Normal file
65
crates/misc/dotnet/src/MutableGlobal.cs
Normal file
@@ -0,0 +1,65 @@
|
||||
using System;
|
||||
|
||||
namespace Wasmtime
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a mutable WebAssembly global value.
|
||||
/// </summary>
|
||||
public class MutableGlobal<T>
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="MutableGlobal<T>" /> with the given initial value.
|
||||
/// </summary>
|
||||
/// <param name="initialValue">The initial value of the global.</param>
|
||||
public MutableGlobal(T initialValue)
|
||||
{
|
||||
InitialValue = initialValue;
|
||||
Kind = Interop.ToValueKind(typeof(T));
|
||||
}
|
||||
|
||||
/// <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);
|
||||
|
||||
// TODO: figure out a way that doesn't box the value
|
||||
return (T)Interop.ToObject(v);
|
||||
}
|
||||
}
|
||||
set
|
||||
{
|
||||
if (Handle is null)
|
||||
{
|
||||
throw new InvalidOperationException("The global cannot be used before it is instantiated.");
|
||||
}
|
||||
|
||||
// TODO: figure out a way that doesn't box the value
|
||||
var v = Interop.ToValue(value, Kind);
|
||||
|
||||
unsafe
|
||||
{
|
||||
Interop.wasm_global_set(Handle.DangerousGetHandle(), &v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal ValueKind Kind { get; private set; }
|
||||
|
||||
internal Interop.GlobalHandle Handle { get; set; }
|
||||
|
||||
internal T InitialValue { get; private set; }
|
||||
}
|
||||
}
|
||||
75
crates/misc/dotnet/src/Store.cs
Normal file
75
crates/misc/dotnet/src/Store.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
|
||||
namespace Wasmtime
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the Wasmtime store.
|
||||
/// </summary>
|
||||
public sealed class Store : IDisposable
|
||||
{
|
||||
internal Store(Engine engine)
|
||||
{
|
||||
Handle = Interop.wasm_store_new(engine.Handle);
|
||||
|
||||
if (Handle.IsInvalid)
|
||||
{
|
||||
throw new WasmtimeException("Failed to create Wasmtime store.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create 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>Retuw <see cref="Module"/>.</returns>
|
||||
public Module CreateModule(string name, byte[] bytes)
|
||||
{
|
||||
if (string.IsNullOrEmpty(name))
|
||||
{
|
||||
throw new ArgumentNullException(nameof(name));
|
||||
}
|
||||
|
||||
if (bytes is null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(bytes));
|
||||
}
|
||||
|
||||
return new Module(this, name, bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a <see cref="Module"/> given the module name and path to the WebAssembly file.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the module.</param>
|
||||
/// <param name="path">The path to the WebAssembly file.</param>
|
||||
/// <returns>Returns a new <see cref="Module"/>.</returns>
|
||||
public Module CreateModule(string name, string path)
|
||||
{
|
||||
return CreateModule(name, File.ReadAllBytes(path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create 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 CreateModule(string path)
|
||||
{
|
||||
return CreateModule(Path.GetFileNameWithoutExtension(path), path);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void Dispose()
|
||||
{
|
||||
if (!Handle.IsInvalid)
|
||||
{
|
||||
Handle.Dispose();
|
||||
Handle.SetHandleAsInvalid();
|
||||
}
|
||||
}
|
||||
|
||||
internal Interop.StoreHandle Handle { get; private set; }
|
||||
}
|
||||
}
|
||||
41
crates/misc/dotnet/src/TrapException.cs
Normal file
41
crates/misc/dotnet/src/TrapException.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
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 message = Marshal.PtrToStringUTF8((IntPtr)bytes.data, (int)bytes.size - 1 /* remove null */);
|
||||
Interop.wasm_byte_vec_delete(ref bytes);
|
||||
|
||||
Interop.wasm_trap_delete(trap);
|
||||
|
||||
return new TrapException(message);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: expose trap frames
|
||||
}
|
||||
}
|
||||
35
crates/misc/dotnet/src/ValueKind.cs
Normal file
35
crates/misc/dotnet/src/ValueKind.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System;
|
||||
|
||||
namespace Wasmtime
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the possible kinds of WebAssembly values.
|
||||
/// </summary>
|
||||
public enum ValueKind
|
||||
{
|
||||
/// <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,
|
||||
}
|
||||
}
|
||||
77
crates/misc/dotnet/src/Wasmtime.csproj
Normal file
77
crates/misc/dotnet/src/Wasmtime.csproj
Normal file
@@ -0,0 +1,77 @@
|
||||
<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>
|
||||
24
crates/misc/dotnet/src/WasmtimeException.cs
Normal file
24
crates/misc/dotnet/src/WasmtimeException.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
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) { }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user