using System; using System.Reflection; using System.Runtime.InteropServices; using Wasmtime.Imports; namespace Wasmtime.Bindings { /// /// Represents a host global binding. /// internal class GlobalBinding : Binding { /// /// Constructs a new global binding. /// /// The global import of the binding. /// The field the import is bound to. public GlobalBinding(GlobalImport import, FieldInfo field) { if (import is null) { throw new ArgumentNullException(nameof(import)); } if (field is null) { throw new ArgumentNullException(nameof(field)); } Import = import; Field = field; Validate(); } /// /// The global import of the binding. /// public GlobalImport Import { get; private set; } /// /// The field the import is bound to. /// public FieldInfo Field { get; private set; } public override SafeHandle Bind(Store store, IHost host) { unsafe { dynamic global = Field.GetValue(host); if (!(global.Handle is 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) { throw CreateBindingException(Import, Field, "field cannot be static"); } if (!Field.IsInitOnly) { throw CreateBindingException(Import, Field, "field must be readonly"); } if (!Field.FieldType.IsGenericType) { throw CreateBindingException(Import, Field, "field is expected to be of type 'Global'"); } var definition = Field.FieldType.GetGenericTypeDefinition(); if (definition == typeof(Global<>)) { if (Import.IsMutable) { throw CreateBindingException(Import, Field, "the import is mutable (use the 'MutableGlobal' type)"); } } else if (definition == typeof(MutableGlobal<>)) { if (!Import.IsMutable) { throw CreateBindingException(Import, Field, "the import is constant (use the 'Global' type)"); } } else { throw CreateBindingException(Import, Field, "field is expected to be of type 'Global' or 'MutableGlobal'"); } var arg = Field.FieldType.GetGenericArguments()[0]; if (Interop.TryGetValueKind(arg, out var kind)) { if (!Interop.IsMatchingKind(kind, Import.Kind)) { throw CreateBindingException(Import, Field, $"global type argument is expected to be of type '{Interop.ToString(Import.Kind)}'"); } } else { throw CreateBindingException(Import, Field, $"'{arg}' is not a valid global type"); } } } }