From b23c1fffbfe4eafb6394478c31a9e0f86f820850 Mon Sep 17 00:00:00 2001 From: Muhammad Azeez Date: Mon, 2 Dec 2024 20:22:25 +0300 Subject: [PATCH 1/9] feat: use more efficient memory copying functions, add http_response_headers, and better logging --- src/Extism.Pdk.MSBuild/FFIGenerator.cs | 144 +++++++++------ src/Extism.Pdk/Interop.cs | 174 +++++++++++++------ src/Extism.Pdk/Native.cs | 69 +++++--- src/Extism.Pdk/native/extism.c | 232 +++++++++++++------------ 4 files changed, 374 insertions(+), 245 deletions(-) diff --git a/src/Extism.Pdk.MSBuild/FFIGenerator.cs b/src/Extism.Pdk.MSBuild/FFIGenerator.cs index c164719..d23857b 100644 --- a/src/Extism.Pdk.MSBuild/FFIGenerator.cs +++ b/src/Extism.Pdk.MSBuild/FFIGenerator.cs @@ -102,7 +102,6 @@ private List GenerateImports(MethodDefinition[] importedMethods, stri return files; } - private FileEntry GenerateExports(MethodDefinition[] exportedMethods) { var sb = new StringBuilder(); @@ -110,57 +109,106 @@ private FileEntry GenerateExports(MethodDefinition[] exportedMethods) if (exportedMethods.Length > 0) { sb.AppendLine(Preamble); - sb.AppendLine( - """ - // _initialize + + // Add runtime initialization code + sb.AppendLine(""" + // Runtime initialization void mono_wasm_load_runtime(const char* unused, int debug_level); #ifdef WASI_AFTER_RUNTIME_LOADED_DECLARATIONS - // This is supplied from the MSBuild itemgroup @(WasiAfterRuntimeLoaded) WASI_AFTER_RUNTIME_LOADED_DECLARATIONS #endif void initialize_runtime() { mono_wasm_load_runtime("", 0); } - - // end of _initialize """); - sb.AppendLine( - """ + // Add enhanced exception handling utilities + sb.AppendLine(""" void mono_wasm_invoke_method_ref(MonoMethod* method, MonoObject** this_arg_in, void* params[], MonoObject** _out_exc, MonoObject** out_result); - MonoString* mono_object_try_to_string (MonoObject *obj, MonoObject **exc, MonoError *error); + MonoString* mono_object_try_to_string(MonoObject *obj, MonoObject **exc, MonoError *error); void mono_print_unhandled_exception(MonoObject *exc); + MonoObject* mono_get_exception_runtime_wrapped(MonoString* wrapped_exception_type, MonoString* wrapped_exception_message); - MonoMethod* method_extism_print_exception; + // Cache method lookups + MonoMethod* method_extism_print_exception = NULL; + MonoMethod* method_get_exception_message = NULL; + + // Enhanced exception printing that ensures all exceptions are properly handled void extism_print_exception(MonoObject* exc) { if (!method_extism_print_exception) { method_extism_print_exception = lookup_dotnet_method("Extism.Pdk.dll", "Extism", "Native", "PrintException", -1); - - if (method_extism_print_exception == NULL) { - printf("Fatal: Failed to find Extism.Native.PrintException"); + if (!method_extism_print_exception) { + // If we can't find the method, set a basic error + const char* message = "Fatal: Failed to find Extism.Native.PrintException"; + ExtismPointer ptr = extism_alloc(strlen(message)); + memcpy((void*)ptr, message, strlen(message)); + extism_error_set(ptr); + return; } - - assert(method_extism_print_exception); } + // Try to get detailed exception info void* method_params[] = { exc }; - MonoObject* exception = NULL; + MonoObject* nested_exception = NULL; MonoObject* result = NULL; - mono_wasm_invoke_method_ref(method_extism_print_exception, NULL, method_params, &exception, &result); + + mono_wasm_invoke_method_ref(method_extism_print_exception, NULL, method_params, &nested_exception, &result); + if (nested_exception != NULL) { + // If we hit an exception while handling the exception, + // fall back to basic error reporting + MonoError error; + MonoObject* string_exc = NULL; + MonoString* message = mono_object_try_to_string(nested_exception, &string_exc, &error); + + if (!string_exc && message) { + char* utf8_message = mono_string_to_utf8(message); + ExtismPointer ptr = extism_alloc(strlen(utf8_message)); + memcpy((void*)ptr, utf8_message, strlen(utf8_message)); + extism_error_set(ptr); + mono_free(utf8_message); + } else { + const char* fallback = "An exception occurred while handling another exception"; + ExtismPointer ptr = extism_alloc(strlen(fallback)); + memcpy((void*)ptr, fallback, strlen(fallback)); + extism_error_set(ptr); + } + } + } + + // Safe method invocation wrapper + int invoke_method_safely(MonoMethod* method, const char* method_name) + { + void* method_params[] = { }; + MonoObject* exception = NULL; + MonoObject* result = NULL; + + mono_wasm_invoke_method_ref(method, NULL, method_params, &exception, &result); + if (exception != NULL) { - const char* message = "An exception was thrown while trying to print the previous exception. Please check stderr for details."; + // First print to stderr for debugging mono_print_unhandled_exception(exception); + // Then ensure it's properly set as an Extism error + extism_print_exception(exception); + return 1; + } + + // Handle return value + int int_result = 0; + if (result != NULL) { + int_result = *(int*)mono_object_unbox(result); } + + return int_result; } - """); + // Generate the exported function wrappers foreach (var method in exportedMethods) { if (method.Parameters.Count > 0) @@ -171,46 +219,29 @@ void extism_print_exception(MonoObject* exc) var assemblyFileName = method.Module.Assembly.Name.Name + ".dll"; var attribute = method.CustomAttributes.First(a => a.AttributeType.Name == "UnmanagedCallersOnlyAttribute"); - var exportName = attribute.Fields.FirstOrDefault(p => p.Name == "EntryPoint").Argument.Value?.ToString() ?? method.Name; - var parameterCount = method.Parameters.Count; - var methodParams = string.Join(", ", Enumerable.Repeat("NULL", parameterCount)); - var returnType = method.ReturnType.FullName; sb.AppendLine($$""" -MonoMethod* method_{{exportName}}; -__attribute__((export_name("{{exportName}}"))) int {{exportName}}() -{ - initialize_runtime(); - - if (!method_{{exportName}}) - { - method_{{exportName}} = lookup_dotnet_method("{{assemblyFileName}}", "{{method.DeclaringType.Namespace}}", "{{method.DeclaringType.Name}}", "{{method.Name}}", -1); - assert(method_{{exportName}}); - } - - void* method_params[] = { }; - MonoObject* exception = NULL; - MonoObject* result = NULL; - mono_wasm_invoke_method_ref(method_{{exportName}}, NULL, method_params, &exception, &result); - - if (exception != NULL) { - const char* message = "An exception was thrown when calling {{exportName}}. Please check stderr for details."; - mono_print_unhandled_exception(exception); - - extism_print_exception(exception); - return 1; - } - - int int_result = 0; // Default value + MonoMethod* method_{{exportName}}; + __attribute__((export_name("{{exportName}}"))) int {{exportName}}() + { + initialize_runtime(); + + if (!method_{{exportName}}) + { + method_{{exportName}} = lookup_dotnet_method("{{assemblyFileName}}", "{{method.DeclaringType.Namespace}}", "{{method.DeclaringType.Name}}", "{{method.Name}}", -1); + if (!method_{{exportName}}) { + const char* error_message = "Failed to lookup method: {{exportName}}"; + ExtismPointer ptr = extism_alloc(strlen(error_message)); + memcpy((void*)ptr, error_message, strlen(error_message)); + extism_error_set(ptr); + return 1; + } + } - if (result != NULL) { - int_result = *(int*)mono_object_unbox(result); - } - - return int_result; -} -"""); + return invoke_method_safely(method_{{exportName}}, "{{exportName}}"); + } + """); } } @@ -218,6 +249,7 @@ void extism_print_exception(MonoObject* exc) return new FileEntry { Name = "exports.c", Content = sb.ToString() }; } + private string ToImportStatement(MethodDefinition method) { var moduleName = method.PInvokeInfo.Module.Name; diff --git a/src/Extism.Pdk/Interop.cs b/src/Extism.Pdk/Interop.cs index e67517f..224843a 100644 --- a/src/Extism.Pdk/Interop.cs +++ b/src/Extism.Pdk/Interop.cs @@ -1,7 +1,9 @@ using System.Buffers.Binary; using System.Diagnostics.CodeAnalysis; +using System.Reflection.PortableExecutable; using System.Text; using System.Text.Json; +using System.Text.Json.Serialization; using System.Text.Json.Serialization.Metadata; namespace Extism; @@ -15,7 +17,7 @@ public static class Pdk /// Read the input data sent by the host. /// /// The input data as a byte array. - public static byte[] GetInput() + public static unsafe byte[] GetInput() { var length = Native.extism_input_length(); if (length == 0) @@ -24,24 +26,17 @@ public static byte[] GetInput() } var buffer = new byte[length]; - - for (ulong i = 0; i < length; i++) + fixed (byte* ptr = buffer) { - if (length - i >= 8) - { - var x = Native.extism_input_load_u64(i); - BinaryPrimitives.WriteUInt64LittleEndian(buffer.AsSpan((int)i), x); - i += 7; - } - else + if (!Native.extism_load_input(0, ptr, length)) { - buffer[i] = Native.extism_input_load_u8(i); + throw new InvalidOperationException("Failed to load input data"); } } - return buffer; } + /// /// Read the input data sent by the host as a UTF-8 encoded string. /// @@ -72,21 +67,21 @@ public static string GetInputString() /// The memory block containing the output data. public static void SetOutput(MemoryBlock block) { - Native.extism_output_set(block.Offset, block.Length); + if (!Native.extism_output_set_from_handle(block.Offset, 0, block.Length)) + { + throw new InvalidOperationException("Failed to set output data."); + } } /// /// Set the output data to be sent back to the host as a byte buffer. /// /// The byte buffer to set as output data. - public unsafe static void SetOutput(ReadOnlySpan data) + public static unsafe void SetOutput(ReadOnlySpan data) { fixed (byte* ptr = data) { - var len = (ulong)data.Length; - var offs = Native.extism_alloc(len); - Native.extism_store(offs, ptr, len); - Native.extism_output_set(offs, len); + Native.extism_output_buf(ptr, (ulong)data.Length); } } @@ -119,10 +114,13 @@ public static void SetOutputJson(T output, JsonTypeInfo typeInfo) /// Set plugin error /// /// - public static void SetError(string errorMessage) + public static unsafe void SetError(string errorMessage) { - var block = Allocate(errorMessage); - Native.extism_error_set(block.Offset); + var bytes = Encoding.UTF8.GetBytes(errorMessage); + fixed (byte* ptr = bytes) + { + Native.extism_error_set_buf(ptr, (ulong)bytes.Length); + } } /// @@ -133,6 +131,10 @@ public static void SetError(string errorMessage) public static MemoryBlock Allocate(ulong length) { var offset = Native.extism_alloc(length); + if (offset == 0 && length > 0) + { + throw new InvalidOperationException("Failed to allocate memory block."); + } return new MemoryBlock(offset, length); } @@ -153,7 +155,10 @@ public static unsafe MemoryBlock Allocate(ReadOnlySpan buffer) fixed (byte* ptr = buffer) { - Native.extism_store(block.Offset, ptr, (ulong)buffer.Length); + if (!Native.extism_store_to_handle(block.Offset, 0, ptr, (ulong)buffer.Length)) + { + throw new InvalidOperationException("Failed to store memory block."); + } } return block; @@ -203,8 +208,17 @@ public static bool TryGetConfig(string key, [NotNullWhen(true)] out string value /// The memory block containing the log message. public static void Log(LogLevel level, MemoryBlock block) { + if (level < (LogLevel)Native.extism_get_log_level()) + { + return; + } + switch (level) { + case LogLevel.Trace: + Native.extism_log_trace(block.Offset); + break; + case LogLevel.Info: Native.extism_log_info(block.Offset); break; @@ -230,6 +244,11 @@ public static void Log(LogLevel level, MemoryBlock block) /// public static void Log(LogLevel level, string message) { + if (level < (LogLevel)Native.extism_get_log_level()) + { + return; + } + var block = Allocate(message); Log(level, block); } @@ -306,36 +325,31 @@ public static void RemoveVar(string key) /// The HTTP response received from the host. The plugin takes ownership of the memory block and is expected to free it. public static HttpResponse SendRequest(HttpRequest request) { - using var stream = new MemoryStream(); - using (var writer = new Utf8JsonWriter(stream)) - { - writer.WriteStartObject(); - writer.WriteString("url", request.Url.AbsoluteUri); - writer.WriteString("method", Enum.GetName(typeof(HttpMethod), request.Method)); + var requestJson = JsonSerializer.Serialize(request, JsonContext.Default.HttpRequest); - if (request.Headers.Count > 0) - { - writer.WriteStartObject("headers"); - foreach (var kvp in request.Headers) - { - writer.WriteString(kvp.Key, kvp.Value); - } - writer.WriteEndObject(); - } + using var requestBlock = Allocate(requestJson); + using var bodyBlock = Allocate(request.Body); - writer.WriteEndObject(); + var responseOffset = Native.extism_http_request(requestBlock.Offset, bodyBlock.Offset); + if (responseOffset == 0) + { + throw new InvalidOperationException("Failed to send HTTP request."); } - var bytes = stream.ToArray(); + var responseBody = MemoryBlock.Find(responseOffset); + var status = Native.extism_http_status_code(); + var httpResponse = new HttpResponse(responseBody, status); - var requestBlock = Allocate(bytes); - var bodyBlock = Allocate(request.Body); + var headersOffset = Native.extism_http_headers(); + if (headersOffset > 0) + { + using var headersBlock = MemoryBlock.Find(headersOffset); + var headersJson = headersBlock.ReadString(); - var offset = Native.extism_http_request(requestBlock.Offset, bodyBlock.Offset); - var block = MemoryBlock.Find(offset); - var status = Native.extism_http_status_code(); + httpResponse.Headers = JsonSerializer.Deserialize(headersJson, JsonContext.Default.DictionaryStringString) ?? []; + } - return new HttpResponse(block, status); + return httpResponse; } } @@ -345,24 +359,29 @@ public static HttpResponse SendRequest(HttpRequest request) public enum LogLevel { /// - /// Information + /// Trace level logging /// - Info, + Trace = 0, /// - /// Debug + /// Debug level logging /// - Debug, + Debug = 1, /// - /// Warning + /// Information level logging /// - Warn, + Info = 2, /// - /// Error + /// Warning level logging /// - Error + Warn = 3, + + /// + /// Error level logging + /// + Error = 4, } /// @@ -391,21 +410,26 @@ public HttpRequest(string url) /// /// HTTP URL /// + [JsonPropertyName("url")] public Uri Url { get; set; } /// /// HTTP Headers /// + [JsonPropertyName("headers")] public Dictionary Headers { get; } = new(); /// /// HTTP method /// + [JsonPropertyName("method")] + [JsonConverter(typeof(JsonStringEnumConverter))] public HttpMethod Method { get; set; } = HttpMethod.GET; /// /// An optional body. /// + [JsonPropertyName("body")] public byte[] Body { get; set; } = Array.Empty(); } @@ -471,6 +495,11 @@ public HttpResponse(MemoryBlock memory, ushort status) /// public ushort Status { get; set; } + /// + /// HTTP Headers. Make sure HTTP response headers are enabled in the host. + /// + public Dictionary Headers { get; set; } = new(); + /// /// Frees the current memory block. /// @@ -534,6 +563,8 @@ public MemoryBlock(ulong offset, ulong length) /// public unsafe void CopyTo(Span buffer) { + CheckDisposed(); + if ((ulong)buffer.Length < Length) { throw new InvalidOperationException($"Buffer must be at least ${Length} bytes."); @@ -541,7 +572,10 @@ public unsafe void CopyTo(Span buffer) fixed (byte* ptr = buffer) { - Native.extism_load(Offset, ptr, Length); + if (!Native.extism_load_from_handle(Offset, 0, ptr, Length)) + { + throw new InvalidOperationException("Failed to load memory block."); + } } } @@ -562,6 +596,8 @@ public void WriteString(string text) /// public unsafe void WriteBytes(ReadOnlySpan bytes) { + CheckDisposed(); + if ((ulong)bytes.Length > Length) { throw new IndexOutOfRangeException("Memory block is not big enough."); @@ -569,7 +605,10 @@ public unsafe void WriteBytes(ReadOnlySpan bytes) fixed (byte* ptr = bytes) { - Native.extism_store(Offset, ptr, Length); + if (!Native.extism_store_to_handle(Offset, 0, ptr, (ulong)bytes.Length)) + { + throw new InvalidOperationException("Failed to store memory block."); + } } } @@ -579,6 +618,8 @@ public unsafe void WriteBytes(ReadOnlySpan bytes) /// public byte[] ReadBytes() { + CheckDisposed(); + var buffer = new byte[Length]; CopyTo(buffer); @@ -606,6 +647,15 @@ public static MemoryBlock Find(ulong offset) return new MemoryBlock(offset, length); } + private void CheckDisposed() + { + if (_disposed) + { + throw new ObjectDisposedException(nameof(MemoryBlock)); + } + } + + private bool _disposed; /// /// Frees the current memory block. /// @@ -617,6 +667,13 @@ public void Dispose() private void Dispose(bool disposing) { + if (_disposed) + { + return; + } + + _disposed = true; + if (disposing) { // free managed resources @@ -628,3 +685,10 @@ private void Dispose(bool disposing) } } } + +[JsonSerializable(typeof(HttpRequest))] +[JsonSerializable(typeof(Dictionary))] +internal partial class JsonContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/src/Extism.Pdk/Native.cs b/src/Extism.Pdk/Native.cs index 61f6abf..2f4eb1e 100644 --- a/src/Extism.Pdk/Native.cs +++ b/src/Extism.Pdk/Native.cs @@ -1,4 +1,5 @@ -using System.Runtime.InteropServices; +using System; +using System.Runtime.InteropServices; namespace Extism; @@ -11,10 +12,14 @@ internal static void Main() { internal class Native { + // Input handling functions + [DllImport("extism")] + internal static unsafe extern ulong extism_input_offset(); [DllImport("extism")] internal static unsafe extern ulong extism_input_length(); + // Memory management functions [DllImport("extism")] internal static unsafe extern ulong extism_length(ulong offset); @@ -24,39 +29,54 @@ internal class Native [DllImport("extism")] internal static unsafe extern void extism_free(ulong offset); + // Memory operations with better efficiency + [DllImport("extism")] + internal static unsafe extern bool extism_load_from_handle(ulong src, ulong src_offset, byte* dest, ulong n); + + [DllImport("extism")] + internal static unsafe extern bool extism_store_to_handle(ulong dest, ulong dest_offset, byte* buffer, ulong n); + + [DllImport("extism")] + internal static unsafe extern ulong extism_alloc_buf(byte* src, ulong n); + + // Input loading with verification [DllImport("extism")] - internal static unsafe extern byte extism_input_load_u8(ulong index); + internal static unsafe extern bool extism_load_input(ulong src_offset, byte* dest, ulong n); [DllImport("extism")] - internal static unsafe extern ulong extism_input_load_u64(ulong index); + internal static unsafe extern bool extism_load_sz(ulong src, ulong src_offset, byte* dest, ulong n); + // Output functions [DllImport("extism")] internal static unsafe extern void extism_output_set(ulong offset, ulong n); [DllImport("extism")] - internal static unsafe extern void extism_error_set(ulong offset); + internal static unsafe extern bool extism_output_set_from_handle(ulong handle, ulong offset, ulong n); [DllImport("extism")] - internal static unsafe extern ulong extism_config_get(ulong offset); + internal static unsafe extern void extism_output_handle(ulong handle); [DllImport("extism")] - internal static unsafe extern ulong extism_var_get(ulong offset); + internal static unsafe extern void extism_output_buf(byte* src, ulong n); + // Error handling [DllImport("extism")] - internal static unsafe extern void extism_var_set(ulong keyOffset, ulong valueOffset); + internal static unsafe extern void extism_error_set(ulong offset); [DllImport("extism")] - internal static unsafe extern void extism_store_u8(ulong offset, byte value); + internal static unsafe extern void extism_error_set_buf(byte* message, ulong messageLen); + // Configuration and variables [DllImport("extism")] - internal static unsafe extern byte extism_load_u8(ulong offset); + internal static unsafe extern ulong extism_config_get(ulong offset); [DllImport("extism")] - internal static unsafe extern void extism_store_u64(ulong offset, ulong value); + internal static unsafe extern ulong extism_var_get(ulong offset); [DllImport("extism")] - internal static unsafe extern ulong extism_load_u64(ulong offset); + internal static unsafe extern void extism_var_set(ulong keyOffset, ulong valueOffset); + // HTTP functions [DllImport("extism")] internal static unsafe extern ulong extism_http_request(ulong requestOffset, ulong bodyOffset); @@ -64,28 +84,33 @@ internal class Native internal static unsafe extern ushort extism_http_status_code(); [DllImport("extism")] - internal static unsafe extern void extism_log_info(ulong offset); + internal static unsafe extern ulong extism_http_headers(); + // Logging functions with levels [DllImport("extism")] - internal static unsafe extern void extism_log_debug(ulong offset); + internal static unsafe extern int extism_get_log_level(); [DllImport("extism")] - internal static unsafe extern void extism_log_warn(ulong offset); + internal static unsafe extern void extism_log_trace(ulong offset); [DllImport("extism")] - internal static unsafe extern void extism_log_error(ulong offset); + internal static unsafe extern void extism_log_debug(ulong offset); [DllImport("extism")] - internal static unsafe extern void extism_store(ulong offset, byte* buffer, ulong n); + internal static unsafe extern void extism_log_info(ulong offset); + [DllImport("extism")] - internal static unsafe extern void extism_load(ulong offset, byte* buffer, ulong n); + internal static unsafe extern void extism_log_warn(ulong offset); + [DllImport("extism")] - internal static unsafe extern void extism_load_input(byte* buffer, ulong n); - - internal static void PrintException(Exception ex) + internal static unsafe extern void extism_log_error(ulong offset); + internal unsafe static void PrintException(Exception ex) { var message = ex.ToString(); - var block = Pdk.Allocate(message); - extism_error_set(block.Offset); + var messageBytes = System.Text.Encoding.UTF8.GetBytes(message); + fixed (byte* ptr = messageBytes) + { + extism_error_set_buf(ptr, (ulong)messageBytes.Length); + } } } diff --git a/src/Extism.Pdk/native/extism.c b/src/Extism.Pdk/native/extism.c index 14d0f42..dd2dc84 100644 --- a/src/Extism.Pdk/native/extism.c +++ b/src/Extism.Pdk/native/extism.c @@ -17,14 +17,23 @@ typedef uint64_t ExtismPointer; -IMPORT("extism:host/env", "input_length") +// Input handling functions +IMPORT("extism:host/env", "input_offset") +extern ExtismPointer extism_input_offset_import(); + +ExtismPointer extism_input_offset() { + return extism_input_offset_import(); +} + +IMPORT("extism:host/env", "input_length") extern uint64_t extism_input_length_import(); uint64_t extism_input_length() { return extism_input_length_import(); } -IMPORT("extism:host/env", "length") +// Memory management +IMPORT("extism:host/env", "length") extern uint64_t extism_length_import(ExtismPointer); uint64_t extism_length(ExtismPointer p) { @@ -38,203 +47,203 @@ ExtismPointer extism_alloc(uint64_t size) { return extism_alloc_import(size); } -IMPORT("extism:host/env", "free") +IMPORT("extism:host/env", "free") extern void extism_free_import(ExtismPointer p); void extism_free(ExtismPointer p) { extism_free_import(p); } -IMPORT("extism:host/env", "input_load_u8") -extern uint8_t extism_input_load_u8_import(ExtismPointer p); +// Memory operations with verification +IMPORT("extism:host/env", "load_from_handle") +extern bool extism_load_from_handle_import(ExtismPointer src, uint64_t src_offset, uint8_t* dest, uint64_t n); + +bool extism_load_from_handle(ExtismPointer src, uint64_t src_offset, uint8_t* dest, uint64_t n) { + return extism_load_from_handle_import(src, src_offset, dest, n); +} + +IMPORT("extism:host/env", "store_to_handle") +extern bool extism_store_to_handle_import(ExtismPointer dest, uint64_t dest_offset, const uint8_t* buffer, uint64_t n); + +bool extism_store_to_handle(ExtismPointer dest, uint64_t dest_offset, const uint8_t* buffer, uint64_t n) { + return extism_store_to_handle_import(dest, dest_offset, buffer, n); +} + +// Buffer allocation and management +IMPORT("extism:host/env", "alloc_buf") +extern ExtismPointer extism_alloc_buf_import(const uint8_t* src, uint64_t n); + +ExtismPointer extism_alloc_buf(const uint8_t* src, uint64_t n) { + return extism_alloc_buf_import(src, n); +} + +// Input loading with verification +IMPORT("extism:host/env", "load_input") +extern bool extism_load_input_import(uint64_t src_offset, uint8_t* dest, uint64_t n); -uint8_t extism_input_load_u8(ExtismPointer p) { - return extism_input_load_u8_import(p); +bool extism_load_input(uint64_t src_offset, uint8_t* dest, uint64_t n) { + return extism_load_input_import(src_offset, dest, n); } -IMPORT("extism:host/env", "input_load_u64") -extern uint64_t extism_input_load_u64_import(ExtismPointer p); +IMPORT("extism:host/env", "load_sz") +extern bool extism_load_sz_import(ExtismPointer src, uint64_t src_offset, uint8_t* dest, uint64_t n); -uint64_t extism_input_load_u64(ExtismPointer p) { - return extism_input_load_u64_import(p); +bool extism_load_sz(ExtismPointer src, uint64_t src_offset, uint8_t* dest, uint64_t n) { + return extism_load_sz_import(src, src_offset, dest, n); } -IMPORT("extism:host/env", "output_set") +// Output functions +IMPORT("extism:host/env", "output_set") extern void extism_output_set_import(ExtismPointer p, uint64_t value); void extism_output_set(ExtismPointer p, uint64_t value) { extism_output_set_import(p, value); } -IMPORT("extism:host/env", "error_set") +IMPORT("extism:host/env", "output_set_from_handle") +extern bool extism_output_set_from_handle_import(ExtismPointer handle, uint64_t offset, uint64_t n); + +bool extism_output_set_from_handle(ExtismPointer handle, uint64_t offset, uint64_t n) { + return extism_output_set_from_handle_import(handle, offset, n); +} + +IMPORT("extism:host/env", "output_handle") +extern void extism_output_handle_import(ExtismPointer handle); + +void extism_output_handle(ExtismPointer handle) { + extism_output_handle_import(handle); +} + +IMPORT("extism:host/env", "output_buf") +extern void extism_output_buf_import(const uint8_t* src, uint64_t n); + +void extism_output_buf(const uint8_t* src, uint64_t n) { + extism_output_buf_import(src, n); +} + +// Error handling +IMPORT("extism:host/env", "error_set") extern void extism_error_set_import(ExtismPointer p); void extism_error_set(ExtismPointer p) { extism_error_set_import(p); } -IMPORT("extism:host/env", "config_get") +IMPORT("extism:host/env", "error_set_buf") +extern void extism_error_set_buf_import(const uint8_t* message, uint64_t messageLen); + +void extism_error_set_buf(const uint8_t* message, uint64_t messageLen) { + extism_error_set_buf_import(message, messageLen); +} + +// Configuration and variables +IMPORT("extism:host/env", "config_get") extern ExtismPointer extism_config_get_import(ExtismPointer p); ExtismPointer extism_config_get(ExtismPointer p) { return extism_config_get_import(p); } -IMPORT("extism:host/env", "var_get") +IMPORT("extism:host/env", "var_get") extern ExtismPointer extism_var_get_import(ExtismPointer p); ExtismPointer extism_var_get(ExtismPointer p) { return extism_var_get_import(p); } -IMPORT("extism:host/env", "var_set") +IMPORT("extism:host/env", "var_set") extern void extism_var_set_import(ExtismPointer p1, ExtismPointer p2); void extism_var_set(ExtismPointer p1, ExtismPointer p2) { extism_var_set_import(p1, p2); } -IMPORT("extism:host/env", "store_u8") -extern void extism_store_u8_import(ExtismPointer p, uint8_t value); +// HTTP functions +IMPORT("extism:host/env", "http_request") +extern ExtismPointer extism_http_request_import(ExtismPointer p1, ExtismPointer p2); -void extism_store_u8(ExtismPointer p, uint8_t value) { - extism_store_u8_import(p, value); +ExtismPointer extism_http_request(ExtismPointer p1, ExtismPointer p2) { + return extism_http_request_import(p1, p2); } -IMPORT("extism:host/env", "load_u8") -extern uint8_t extism_load_u8_import(ExtismPointer p); +IMPORT("extism:host/env", "http_status_code") +extern int32_t extism_http_status_code_import(); -uint8_t extism_load_u8(ExtismPointer p) { - return extism_load_u8_import(p); +int32_t extism_http_status_code() { + return extism_http_status_code_import(); } -IMPORT("extism:host/env", "store_u64") -extern void extism_store_u64_import(ExtismPointer p, uint64_t value); +IMPORT("extism:host/env", "http_headers") +extern ExtismPointer extism_http_headers_import(); -void extism_store_u64(ExtismPointer p, uint64_t value) { - extism_store_u64_import(p, value); +ExtismPointer extism_http_headers() { + return extism_http_headers_import(); } -IMPORT("extism:host/env", "load_u64") -extern uint64_t extism_load_u64_import(ExtismPointer p); +// Logging functions +IMPORT("extism:host/env", "get_log_level") +extern int32_t extism_get_log_level_import(); -uint64_t extism_load_u64(ExtismPointer p) { - return extism_load_u64_import(p); +int32_t extism_get_log_level() { + return extism_get_log_level_import(); } -IMPORT("extism:host/env", "http_request") -extern ExtismPointer extism_http_request_import(ExtismPointer p1, ExtismPointer p2); +IMPORT("extism:host/env", "log_trace") +extern void extism_log_trace_import(ExtismPointer p); -ExtismPointer extism_http_request(ExtismPointer p1, ExtismPointer p2) { - return extism_http_request_import(p1, p2); +void extism_log_trace(ExtismPointer p) { + extism_log_trace_import(p); } -IMPORT("extism:host/env", "http_status_code") -extern int32_t extism_http_status_code_import(); +IMPORT("extism:host/env", "log_debug") +extern void extism_log_debug_import(ExtismPointer p); -int32_t extism_http_status_code() { - return extism_http_status_code_import(); +void extism_log_debug(ExtismPointer p) { + extism_log_debug_import(p); } -IMPORT("extism:host/env", "log_info") +IMPORT("extism:host/env", "log_info") extern void extism_log_info_import(ExtismPointer p); void extism_log_info(ExtismPointer p) { extism_log_info_import(p); } -IMPORT("extism:host/env", "log_debug") -extern void extism_log_debug_import(ExtismPointer p); - -void extism_log_debug(ExtismPointer p) { - extism_log_debug_import(p); -} - -IMPORT("extism:host/env", "log_warn") +IMPORT("extism:host/env", "log_warn") extern void extism_log_warn_import(ExtismPointer p); void extism_log_warn(ExtismPointer p) { extism_log_warn_import(p); } -IMPORT("extism:host/env", "log_error") +IMPORT("extism:host/env", "log_error") extern void extism_log_error_import(ExtismPointer p); void extism_log_error(ExtismPointer p) { extism_log_error_import(p); } -void extism_load(uint64_t offs, uint8_t* buffer, uint64_t length) { - uint64_t n; - uint64_t left = 0; - - for (uint64_t i = 0; i < length; i += 1) { - left = length - i; - if (left < 8) { - buffer[i] = extism_load_u8(offs + i); - continue; - } - - n = extism_load_u64(offs + i); - *((uint64_t*)buffer + (i / 8)) = n; - i += 7; - } -} - -void extism_load_input(uint8_t* buffer, uint64_t length) { - uint64_t n; - uint64_t left = 0; - - for (uint64_t i = 0; i < length; i += 1) { - left = length - i; - if (left < 8) { - buffer[i] = extism_input_load_u8(i); - continue; - } - - n = extism_input_load_u64(i); - *((uint64_t*)buffer + (i / 8)) = n; - i += 7; - } -} - -void extism_store(uint64_t offs, const uint8_t* buffer, uint64_t length) { - uint64_t n; - uint64_t left = 0; - for (uint64_t i = 0; i < length; i++) { - left = length - i; - if (left < 8) { - extism_store_u8(offs + i, buffer[i]); - continue; - } - - n = *((uint64_t*)buffer + (i / 8)); - extism_store_u64(offs + i, n); - i += 7; - } -} - // Wrap mono_runtime_run_main so that we ensure at least one argument is passed in to Mono // otherwise it crashes, we use the -Wl,--wrap flag to instruct the linker to replace mono_runtime_run_main with // __wrap_mono_runtime_run_main everywhere. see: // - build/Extism.Pdk.targets // - https://gist.github.com/mlabbe/a0b7b14be652085341162321a0a08530 // - https://github.com/dotnet/runtime/blob/4101144c8dde177addfb93ac46425fd1a8604f7a/src/mono/mono/metadata/object.c#L4175 -int __real_mono_runtime_run_main(MonoMethod *method, int argc, char *argv[], MonoObject **exc); +int __real_mono_runtime_run_main(MonoMethod* method, int argc, char* argv[], MonoObject** exc); -int __wrap_mono_runtime_run_main(MonoMethod *method, int argc, char *argv[], MonoObject **exc) +int __wrap_mono_runtime_run_main(MonoMethod* method, int argc, char* argv[], MonoObject** exc) { - if (argc == 0) - { - char *temp[] = {"extism", NULL}; - argv = temp; - argc = 1; - } - - return __real_mono_runtime_run_main(method, argc, argv, exc); + if (argc == 0) + { + char* temp[] = { "extism", NULL }; + argv = temp; + argc = 1; + } + return __real_mono_runtime_run_main(method, argc, argv, exc); } // Wrap mono_wasm_load_runtime to make sure we don't initialize mono more than once + void __real_mono_wasm_load_runtime(const char* unused, int debug_level); bool mono_runtime_initialized = false; @@ -242,7 +251,6 @@ void __wrap_mono_wasm_load_runtime(const char* unused, int debug_level) { if (mono_runtime_initialized) { return; } - __real_mono_wasm_load_runtime(unused, debug_level); mono_runtime_initialized = true; -} +} \ No newline at end of file From eb4aa710d61b9cf307d25deb2d1a0919bba3bb7f Mon Sep 17 00:00:00 2001 From: Muhammad Azeez Date: Mon, 2 Dec 2024 20:31:49 +0300 Subject: [PATCH 2/9] revert FFIGenerator --- src/Extism.Pdk.MSBuild/FFIGenerator.cs | 146 ++++++++++--------------- 1 file changed, 57 insertions(+), 89 deletions(-) diff --git a/src/Extism.Pdk.MSBuild/FFIGenerator.cs b/src/Extism.Pdk.MSBuild/FFIGenerator.cs index d23857b..7492fbc 100644 --- a/src/Extism.Pdk.MSBuild/FFIGenerator.cs +++ b/src/Extism.Pdk.MSBuild/FFIGenerator.cs @@ -102,6 +102,7 @@ private List GenerateImports(MethodDefinition[] importedMethods, stri return files; } + private FileEntry GenerateExports(MethodDefinition[] exportedMethods) { var sb = new StringBuilder(); @@ -109,106 +110,57 @@ private FileEntry GenerateExports(MethodDefinition[] exportedMethods) if (exportedMethods.Length > 0) { sb.AppendLine(Preamble); - - // Add runtime initialization code - sb.AppendLine(""" - // Runtime initialization + sb.AppendLine( + """ + // _initialize void mono_wasm_load_runtime(const char* unused, int debug_level); #ifdef WASI_AFTER_RUNTIME_LOADED_DECLARATIONS + // This is supplied from the MSBuild itemgroup @(WasiAfterRuntimeLoaded) WASI_AFTER_RUNTIME_LOADED_DECLARATIONS #endif void initialize_runtime() { mono_wasm_load_runtime("", 0); } + + // end of _initialize """); - // Add enhanced exception handling utilities - sb.AppendLine(""" + sb.AppendLine( + """ void mono_wasm_invoke_method_ref(MonoMethod* method, MonoObject** this_arg_in, void* params[], MonoObject** _out_exc, MonoObject** out_result); - MonoString* mono_object_try_to_string(MonoObject *obj, MonoObject **exc, MonoError *error); + MonoString* mono_object_try_to_string (MonoObject *obj, MonoObject **exc, MonoError *error); void mono_print_unhandled_exception(MonoObject *exc); - MonoObject* mono_get_exception_runtime_wrapped(MonoString* wrapped_exception_type, MonoString* wrapped_exception_message); - // Cache method lookups - MonoMethod* method_extism_print_exception = NULL; - MonoMethod* method_get_exception_message = NULL; - - // Enhanced exception printing that ensures all exceptions are properly handled + MonoMethod* method_extism_print_exception; void extism_print_exception(MonoObject* exc) { if (!method_extism_print_exception) { method_extism_print_exception = lookup_dotnet_method("Extism.Pdk.dll", "Extism", "Native", "PrintException", -1); - if (!method_extism_print_exception) { - // If we can't find the method, set a basic error - const char* message = "Fatal: Failed to find Extism.Native.PrintException"; - ExtismPointer ptr = extism_alloc(strlen(message)); - memcpy((void*)ptr, message, strlen(message)); - extism_error_set(ptr); - return; - } - } - // Try to get detailed exception info - void* method_params[] = { exc }; - MonoObject* nested_exception = NULL; - MonoObject* result = NULL; - - mono_wasm_invoke_method_ref(method_extism_print_exception, NULL, method_params, &nested_exception, &result); - - if (nested_exception != NULL) { - // If we hit an exception while handling the exception, - // fall back to basic error reporting - MonoError error; - MonoObject* string_exc = NULL; - MonoString* message = mono_object_try_to_string(nested_exception, &string_exc, &error); - - if (!string_exc && message) { - char* utf8_message = mono_string_to_utf8(message); - ExtismPointer ptr = extism_alloc(strlen(utf8_message)); - memcpy((void*)ptr, utf8_message, strlen(utf8_message)); - extism_error_set(ptr); - mono_free(utf8_message); - } else { - const char* fallback = "An exception occurred while handling another exception"; - ExtismPointer ptr = extism_alloc(strlen(fallback)); - memcpy((void*)ptr, fallback, strlen(fallback)); - extism_error_set(ptr); + if (method_extism_print_exception == NULL) { + printf("Fatal: Failed to find Extism.Native.PrintException"); } + + assert(method_extism_print_exception); } - } - // Safe method invocation wrapper - int invoke_method_safely(MonoMethod* method, const char* method_name) - { - void* method_params[] = { }; + void* method_params[] = { exc }; MonoObject* exception = NULL; MonoObject* result = NULL; - - mono_wasm_invoke_method_ref(method, NULL, method_params, &exception, &result); - + mono_wasm_invoke_method_ref(method_extism_print_exception, NULL, method_params, &exception, &result); + if (exception != NULL) { - // First print to stderr for debugging + const char* message = "An exception was thrown while trying to print the previous exception. Please check stderr for details."; mono_print_unhandled_exception(exception); - // Then ensure it's properly set as an Extism error - extism_print_exception(exception); - return 1; - } - - // Handle return value - int int_result = 0; - if (result != NULL) { - int_result = *(int*)mono_object_unbox(result); } - - return int_result; } + """); - // Generate the exported function wrappers foreach (var method in exportedMethods) { if (method.Parameters.Count > 0) @@ -219,29 +171,46 @@ int invoke_method_safely(MonoMethod* method, const char* method_name) var assemblyFileName = method.Module.Assembly.Name.Name + ".dll"; var attribute = method.CustomAttributes.First(a => a.AttributeType.Name == "UnmanagedCallersOnlyAttribute"); + var exportName = attribute.Fields.FirstOrDefault(p => p.Name == "EntryPoint").Argument.Value?.ToString() ?? method.Name; + var parameterCount = method.Parameters.Count; + var methodParams = string.Join(", ", Enumerable.Repeat("NULL", parameterCount)); + var returnType = method.ReturnType.FullName; sb.AppendLine($$""" - MonoMethod* method_{{exportName}}; - __attribute__((export_name("{{exportName}}"))) int {{exportName}}() - { - initialize_runtime(); - - if (!method_{{exportName}}) - { - method_{{exportName}} = lookup_dotnet_method("{{assemblyFileName}}", "{{method.DeclaringType.Namespace}}", "{{method.DeclaringType.Name}}", "{{method.Name}}", -1); - if (!method_{{exportName}}) { - const char* error_message = "Failed to lookup method: {{exportName}}"; - ExtismPointer ptr = extism_alloc(strlen(error_message)); - memcpy((void*)ptr, error_message, strlen(error_message)); - extism_error_set(ptr); - return 1; - } - } +MonoMethod* method_{{exportName}}; +__attribute__((export_name("{{exportName}}"))) int {{exportName}}() +{ + initialize_runtime(); - return invoke_method_safely(method_{{exportName}}, "{{exportName}}"); - } - """); + if (!method_{{exportName}}) + { + method_{{exportName}} = lookup_dotnet_method("{{assemblyFileName}}", "{{method.DeclaringType.Namespace}}", "{{method.DeclaringType.Name}}", "{{method.Name}}", -1); + assert(method_{{exportName}}); + } + + void* method_params[] = { }; + MonoObject* exception = NULL; + MonoObject* result = NULL; + mono_wasm_invoke_method_ref(method_{{exportName}}, NULL, method_params, &exception, &result); + + if (exception != NULL) { + const char* message = "An exception was thrown when calling {{exportName}}. Please check stderr for details."; + mono_print_unhandled_exception(exception); + + extism_print_exception(exception); + return 1; + } + + int int_result = 0; // Default value + + if (result != NULL) { + int_result = *(int*)mono_object_unbox(result); + } + + return int_result; +} +"""); } } @@ -249,7 +218,6 @@ int invoke_method_safely(MonoMethod* method, const char* method_name) return new FileEntry { Name = "exports.c", Content = sb.ToString() }; } - private string ToImportStatement(MethodDefinition method) { var moduleName = method.PInvokeInfo.Module.Name; @@ -347,4 +315,4 @@ public class FileEntry /// public string Content { get; set; } = default!; } -} +} \ No newline at end of file From c14fef624a9ec09ca500b635e9a8977f54b594d6 Mon Sep 17 00:00:00 2001 From: Muhammad Azeez Date: Mon, 2 Dec 2024 20:58:31 +0300 Subject: [PATCH 3/9] revert memory operations --- src/Extism.Pdk/Interop.cs | 48 ++++--- src/Extism.Pdk/Native.cs | 68 ++++------ src/Extism.Pdk/native/extism.c | 224 +++++++++++++++++---------------- 3 files changed, 168 insertions(+), 172 deletions(-) diff --git a/src/Extism.Pdk/Interop.cs b/src/Extism.Pdk/Interop.cs index 224843a..8d22434 100644 --- a/src/Extism.Pdk/Interop.cs +++ b/src/Extism.Pdk/Interop.cs @@ -26,13 +26,21 @@ public static unsafe byte[] GetInput() } var buffer = new byte[length]; - fixed (byte* ptr = buffer) + + for (ulong i = 0; i < length; i++) { - if (!Native.extism_load_input(0, ptr, length)) + if (length - i >= 8) + { + var x = Native.extism_input_load_u64(i); + BinaryPrimitives.WriteUInt64LittleEndian(buffer.AsSpan((int)i), x); + i += 7; + } + else { - throw new InvalidOperationException("Failed to load input data"); + buffer[i] = Native.extism_input_load_u8(i); } } + return buffer; } @@ -67,10 +75,7 @@ public static string GetInputString() /// The memory block containing the output data. public static void SetOutput(MemoryBlock block) { - if (!Native.extism_output_set_from_handle(block.Offset, 0, block.Length)) - { - throw new InvalidOperationException("Failed to set output data."); - } + Native.extism_output_set(block.Offset, block.Length); } /// @@ -81,7 +86,10 @@ public static unsafe void SetOutput(ReadOnlySpan data) { fixed (byte* ptr = data) { - Native.extism_output_buf(ptr, (ulong)data.Length); + var len = (ulong)data.Length; + var offs = Native.extism_alloc(len); + Native.extism_store(offs, ptr, len); + Native.extism_output_set(offs, len); } } @@ -116,11 +124,8 @@ public static void SetOutputJson(T output, JsonTypeInfo typeInfo) /// public static unsafe void SetError(string errorMessage) { - var bytes = Encoding.UTF8.GetBytes(errorMessage); - fixed (byte* ptr = bytes) - { - Native.extism_error_set_buf(ptr, (ulong)bytes.Length); - } + var block = Allocate(errorMessage); + Native.extism_error_set(block.Offset); } /// @@ -155,10 +160,7 @@ public static unsafe MemoryBlock Allocate(ReadOnlySpan buffer) fixed (byte* ptr = buffer) { - if (!Native.extism_store_to_handle(block.Offset, 0, ptr, (ulong)buffer.Length)) - { - throw new InvalidOperationException("Failed to store memory block."); - } + Native.extism_store(block.Offset, ptr, (ulong)buffer.Length); } return block; @@ -572,10 +574,7 @@ public unsafe void CopyTo(Span buffer) fixed (byte* ptr = buffer) { - if (!Native.extism_load_from_handle(Offset, 0, ptr, Length)) - { - throw new InvalidOperationException("Failed to load memory block."); - } + Native.extism_load(Offset, ptr, Length); } } @@ -598,17 +597,14 @@ public unsafe void WriteBytes(ReadOnlySpan bytes) { CheckDisposed(); - if ((ulong)bytes.Length > Length) + if((ulong)bytes.Length > Length) { throw new IndexOutOfRangeException("Memory block is not big enough."); } fixed (byte* ptr = bytes) { - if (!Native.extism_store_to_handle(Offset, 0, ptr, (ulong)bytes.Length)) - { - throw new InvalidOperationException("Failed to store memory block."); - } + Native.extism_store(Offset, ptr, Length); } } diff --git a/src/Extism.Pdk/Native.cs b/src/Extism.Pdk/Native.cs index 2f4eb1e..86a1713 100644 --- a/src/Extism.Pdk/Native.cs +++ b/src/Extism.Pdk/Native.cs @@ -12,14 +12,10 @@ internal static void Main() { internal class Native { - // Input handling functions - [DllImport("extism")] - internal static unsafe extern ulong extism_input_offset(); [DllImport("extism")] internal static unsafe extern ulong extism_input_length(); - // Memory management functions [DllImport("extism")] internal static unsafe extern ulong extism_length(ulong offset); @@ -29,88 +25,78 @@ internal class Native [DllImport("extism")] internal static unsafe extern void extism_free(ulong offset); - // Memory operations with better efficiency [DllImport("extism")] - internal static unsafe extern bool extism_load_from_handle(ulong src, ulong src_offset, byte* dest, ulong n); + internal static unsafe extern byte extism_input_load_u8(ulong index); [DllImport("extism")] - internal static unsafe extern bool extism_store_to_handle(ulong dest, ulong dest_offset, byte* buffer, ulong n); + internal static unsafe extern ulong extism_input_load_u64(ulong index); [DllImport("extism")] - internal static unsafe extern ulong extism_alloc_buf(byte* src, ulong n); + internal static unsafe extern void extism_output_set(ulong offset, ulong n); - // Input loading with verification [DllImport("extism")] - internal static unsafe extern bool extism_load_input(ulong src_offset, byte* dest, ulong n); + internal static unsafe extern void extism_error_set(ulong offset); [DllImport("extism")] - internal static unsafe extern bool extism_load_sz(ulong src, ulong src_offset, byte* dest, ulong n); + internal static unsafe extern ulong extism_config_get(ulong offset); - // Output functions [DllImport("extism")] - internal static unsafe extern void extism_output_set(ulong offset, ulong n); + internal static unsafe extern ulong extism_var_get(ulong offset); [DllImport("extism")] - internal static unsafe extern bool extism_output_set_from_handle(ulong handle, ulong offset, ulong n); + internal static unsafe extern void extism_var_set(ulong keyOffset, ulong valueOffset); [DllImport("extism")] - internal static unsafe extern void extism_output_handle(ulong handle); + internal static unsafe extern void extism_store_u8(ulong offset, byte value); [DllImport("extism")] - internal static unsafe extern void extism_output_buf(byte* src, ulong n); + internal static unsafe extern byte extism_load_u8(ulong offset); - // Error handling [DllImport("extism")] - internal static unsafe extern void extism_error_set(ulong offset); + internal static unsafe extern void extism_store_u64(ulong offset, ulong value); [DllImport("extism")] - internal static unsafe extern void extism_error_set_buf(byte* message, ulong messageLen); + internal static unsafe extern ulong extism_load_u64(ulong offset); - // Configuration and variables [DllImport("extism")] - internal static unsafe extern ulong extism_config_get(ulong offset); + internal static unsafe extern ulong extism_http_request(ulong requestOffset, ulong bodyOffset); [DllImport("extism")] - internal static unsafe extern ulong extism_var_get(ulong offset); + internal static unsafe extern ushort extism_http_status_code(); [DllImport("extism")] - internal static unsafe extern void extism_var_set(ulong keyOffset, ulong valueOffset); + internal static unsafe extern void extism_log_info(ulong offset); - // HTTP functions [DllImport("extism")] - internal static unsafe extern ulong extism_http_request(ulong requestOffset, ulong bodyOffset); + internal static unsafe extern void extism_log_debug(ulong offset); [DllImport("extism")] - internal static unsafe extern ushort extism_http_status_code(); + internal static unsafe extern void extism_log_warn(ulong offset); [DllImport("extism")] - internal static unsafe extern ulong extism_http_headers(); + internal static unsafe extern void extism_log_error(ulong offset); - // Logging functions with levels [DllImport("extism")] - internal static unsafe extern int extism_get_log_level(); - + internal static unsafe extern void extism_store(ulong offset, byte* buffer, ulong n); [DllImport("extism")] - internal static unsafe extern void extism_log_trace(ulong offset); - + internal static unsafe extern void extism_load(ulong offset, byte* buffer, ulong n); [DllImport("extism")] - internal static unsafe extern void extism_log_debug(ulong offset); + internal static unsafe extern void extism_load_input(byte* buffer, ulong n); [DllImport("extism")] - internal static unsafe extern void extism_log_info(ulong offset); + internal static unsafe extern ulong extism_http_headers(); + // Logging functions with levels [DllImport("extism")] - internal static unsafe extern void extism_log_warn(ulong offset); + internal static unsafe extern int extism_get_log_level(); [DllImport("extism")] - internal static unsafe extern void extism_log_error(ulong offset); + internal static unsafe extern void extism_log_trace(ulong offset); + internal unsafe static void PrintException(Exception ex) { var message = ex.ToString(); - var messageBytes = System.Text.Encoding.UTF8.GetBytes(message); - fixed (byte* ptr = messageBytes) - { - extism_error_set_buf(ptr, (ulong)messageBytes.Length); - } + var block = Pdk.Allocate(message); + extism_error_set(block.Offset); } } diff --git a/src/Extism.Pdk/native/extism.c b/src/Extism.Pdk/native/extism.c index dd2dc84..3959559 100644 --- a/src/Extism.Pdk/native/extism.c +++ b/src/Extism.Pdk/native/extism.c @@ -1,4 +1,4 @@ -#include +#include #include #include @@ -17,167 +17,130 @@ typedef uint64_t ExtismPointer; -// Input handling functions -IMPORT("extism:host/env", "input_offset") -extern ExtismPointer extism_input_offset_import(); - -ExtismPointer extism_input_offset() { - return extism_input_offset_import(); -} - IMPORT("extism:host/env", "input_length") extern uint64_t extism_input_length_import(); uint64_t extism_input_length() { - return extism_input_length_import(); + return extism_input_length_import(); } -// Memory management IMPORT("extism:host/env", "length") extern uint64_t extism_length_import(ExtismPointer); uint64_t extism_length(ExtismPointer p) { - return extism_length_import(p); + return extism_length_import(p); } IMPORT("extism:host/env", "alloc") extern ExtismPointer extism_alloc_import(uint64_t size); ExtismPointer extism_alloc(uint64_t size) { - return extism_alloc_import(size); + return extism_alloc_import(size); } IMPORT("extism:host/env", "free") extern void extism_free_import(ExtismPointer p); void extism_free(ExtismPointer p) { - extism_free_import(p); -} - -// Memory operations with verification -IMPORT("extism:host/env", "load_from_handle") -extern bool extism_load_from_handle_import(ExtismPointer src, uint64_t src_offset, uint8_t* dest, uint64_t n); - -bool extism_load_from_handle(ExtismPointer src, uint64_t src_offset, uint8_t* dest, uint64_t n) { - return extism_load_from_handle_import(src, src_offset, dest, n); -} - -IMPORT("extism:host/env", "store_to_handle") -extern bool extism_store_to_handle_import(ExtismPointer dest, uint64_t dest_offset, const uint8_t* buffer, uint64_t n); - -bool extism_store_to_handle(ExtismPointer dest, uint64_t dest_offset, const uint8_t* buffer, uint64_t n) { - return extism_store_to_handle_import(dest, dest_offset, buffer, n); -} - -// Buffer allocation and management -IMPORT("extism:host/env", "alloc_buf") -extern ExtismPointer extism_alloc_buf_import(const uint8_t* src, uint64_t n); - -ExtismPointer extism_alloc_buf(const uint8_t* src, uint64_t n) { - return extism_alloc_buf_import(src, n); + extism_free_import(p); } -// Input loading with verification -IMPORT("extism:host/env", "load_input") -extern bool extism_load_input_import(uint64_t src_offset, uint8_t* dest, uint64_t n); +IMPORT("extism:host/env", "input_load_u8") +extern uint8_t extism_input_load_u8_import(ExtismPointer p); -bool extism_load_input(uint64_t src_offset, uint8_t* dest, uint64_t n) { - return extism_load_input_import(src_offset, dest, n); +uint8_t extism_input_load_u8(ExtismPointer p) { + return extism_input_load_u8_import(p); } -IMPORT("extism:host/env", "load_sz") -extern bool extism_load_sz_import(ExtismPointer src, uint64_t src_offset, uint8_t* dest, uint64_t n); +IMPORT("extism:host/env", "input_load_u64") +extern uint64_t extism_input_load_u64_import(ExtismPointer p); -bool extism_load_sz(ExtismPointer src, uint64_t src_offset, uint8_t* dest, uint64_t n) { - return extism_load_sz_import(src, src_offset, dest, n); +uint64_t extism_input_load_u64(ExtismPointer p) { + return extism_input_load_u64_import(p); } -// Output functions IMPORT("extism:host/env", "output_set") extern void extism_output_set_import(ExtismPointer p, uint64_t value); void extism_output_set(ExtismPointer p, uint64_t value) { - extism_output_set_import(p, value); -} - -IMPORT("extism:host/env", "output_set_from_handle") -extern bool extism_output_set_from_handle_import(ExtismPointer handle, uint64_t offset, uint64_t n); - -bool extism_output_set_from_handle(ExtismPointer handle, uint64_t offset, uint64_t n) { - return extism_output_set_from_handle_import(handle, offset, n); + extism_output_set_import(p, value); } -IMPORT("extism:host/env", "output_handle") -extern void extism_output_handle_import(ExtismPointer handle); - -void extism_output_handle(ExtismPointer handle) { - extism_output_handle_import(handle); -} - -IMPORT("extism:host/env", "output_buf") -extern void extism_output_buf_import(const uint8_t* src, uint64_t n); - -void extism_output_buf(const uint8_t* src, uint64_t n) { - extism_output_buf_import(src, n); -} - -// Error handling IMPORT("extism:host/env", "error_set") extern void extism_error_set_import(ExtismPointer p); void extism_error_set(ExtismPointer p) { - extism_error_set_import(p); -} - -IMPORT("extism:host/env", "error_set_buf") -extern void extism_error_set_buf_import(const uint8_t* message, uint64_t messageLen); - -void extism_error_set_buf(const uint8_t* message, uint64_t messageLen) { - extism_error_set_buf_import(message, messageLen); + extism_error_set_import(p); } -// Configuration and variables IMPORT("extism:host/env", "config_get") extern ExtismPointer extism_config_get_import(ExtismPointer p); ExtismPointer extism_config_get(ExtismPointer p) { - return extism_config_get_import(p); + return extism_config_get_import(p); } IMPORT("extism:host/env", "var_get") extern ExtismPointer extism_var_get_import(ExtismPointer p); ExtismPointer extism_var_get(ExtismPointer p) { - return extism_var_get_import(p); + return extism_var_get_import(p); } IMPORT("extism:host/env", "var_set") extern void extism_var_set_import(ExtismPointer p1, ExtismPointer p2); void extism_var_set(ExtismPointer p1, ExtismPointer p2) { - extism_var_set_import(p1, p2); + extism_var_set_import(p1, p2); +} + +IMPORT("extism:host/env", "store_u8") +extern void extism_store_u8_import(ExtismPointer p, uint8_t value); + +void extism_store_u8(ExtismPointer p, uint8_t value) { + extism_store_u8_import(p, value); +} + +IMPORT("extism:host/env", "load_u8") +extern uint8_t extism_load_u8_import(ExtismPointer p); + +uint8_t extism_load_u8(ExtismPointer p) { + return extism_load_u8_import(p); +} + +IMPORT("extism:host/env", "store_u64") +extern void extism_store_u64_import(ExtismPointer p, uint64_t value); + +void extism_store_u64(ExtismPointer p, uint64_t value) { + extism_store_u64_import(p, value); +} + +IMPORT("extism:host/env", "load_u64") +extern uint64_t extism_load_u64_import(ExtismPointer p); + +uint64_t extism_load_u64(ExtismPointer p) { + return extism_load_u64_import(p); } -// HTTP functions IMPORT("extism:host/env", "http_request") extern ExtismPointer extism_http_request_import(ExtismPointer p1, ExtismPointer p2); ExtismPointer extism_http_request(ExtismPointer p1, ExtismPointer p2) { - return extism_http_request_import(p1, p2); + return extism_http_request_import(p1, p2); } IMPORT("extism:host/env", "http_status_code") extern int32_t extism_http_status_code_import(); int32_t extism_http_status_code() { - return extism_http_status_code_import(); + return extism_http_status_code_import(); } IMPORT("extism:host/env", "http_headers") extern ExtismPointer extism_http_headers_import(); ExtismPointer extism_http_headers() { - return extism_http_headers_import(); + return extism_http_headers_import(); } // Logging functions @@ -185,42 +148,92 @@ IMPORT("extism:host/env", "get_log_level") extern int32_t extism_get_log_level_import(); int32_t extism_get_log_level() { - return extism_get_log_level_import(); + return extism_get_log_level_import(); } IMPORT("extism:host/env", "log_trace") extern void extism_log_trace_import(ExtismPointer p); void extism_log_trace(ExtismPointer p) { - extism_log_trace_import(p); + extism_log_trace_import(p); } IMPORT("extism:host/env", "log_debug") extern void extism_log_debug_import(ExtismPointer p); void extism_log_debug(ExtismPointer p) { - extism_log_debug_import(p); + extism_log_debug_import(p); } IMPORT("extism:host/env", "log_info") extern void extism_log_info_import(ExtismPointer p); void extism_log_info(ExtismPointer p) { - extism_log_info_import(p); + extism_log_info_import(p); } IMPORT("extism:host/env", "log_warn") extern void extism_log_warn_import(ExtismPointer p); void extism_log_warn(ExtismPointer p) { - extism_log_warn_import(p); + extism_log_warn_import(p); } IMPORT("extism:host/env", "log_error") extern void extism_log_error_import(ExtismPointer p); void extism_log_error(ExtismPointer p) { - extism_log_error_import(p); + extism_log_error_import(p); +} + +void extism_load(uint64_t offs, uint8_t* buffer, uint64_t length) { + uint64_t n; + uint64_t left = 0; + + for (uint64_t i = 0; i < length; i += 1) { + left = length - i; + if (left < 8) { + buffer[i] = extism_load_u8(offs + i); + continue; + } + + n = extism_load_u64(offs + i); + *((uint64_t*)buffer + (i / 8)) = n; + i += 7; + } +} + +void extism_load_input(uint8_t* buffer, uint64_t length) { + uint64_t n; + uint64_t left = 0; + + for (uint64_t i = 0; i < length; i += 1) { + left = length - i; + if (left < 8) { + buffer[i] = extism_input_load_u8(i); + continue; + } + + n = extism_input_load_u64(i); + *((uint64_t*)buffer + (i / 8)) = n; + i += 7; + } +} + +void extism_store(uint64_t offs, const uint8_t* buffer, uint64_t length) { + uint64_t n; + uint64_t left = 0; + for (uint64_t i = 0; i < length; i++) { + left = length - i; + if (left < 8) { + extism_store_u8(offs + i, buffer[i]); + continue; + } + + n = *((uint64_t*)buffer + (i / 8)); + extism_store_u64(offs + i, n); + i += 7; + } } // Wrap mono_runtime_run_main so that we ensure at least one argument is passed in to Mono @@ -233,24 +246,25 @@ int __real_mono_runtime_run_main(MonoMethod* method, int argc, char* argv[], Mon int __wrap_mono_runtime_run_main(MonoMethod* method, int argc, char* argv[], MonoObject** exc) { - if (argc == 0) - { - char* temp[] = { "extism", NULL }; - argv = temp; - argc = 1; - } - return __real_mono_runtime_run_main(method, argc, argv, exc); + if (argc == 0) + { + char* temp[] = { "extism", NULL }; + argv = temp; + argc = 1; + } + + return __real_mono_runtime_run_main(method, argc, argv, exc); } // Wrap mono_wasm_load_runtime to make sure we don't initialize mono more than once - void __real_mono_wasm_load_runtime(const char* unused, int debug_level); bool mono_runtime_initialized = false; void __wrap_mono_wasm_load_runtime(const char* unused, int debug_level) { - if (mono_runtime_initialized) { - return; - } - __real_mono_wasm_load_runtime(unused, debug_level); - mono_runtime_initialized = true; + if (mono_runtime_initialized) { + return; + } + + __real_mono_wasm_load_runtime(unused, debug_level); + mono_runtime_initialized = true; } \ No newline at end of file From 6cf2f760b9a3f6d35353651d8ccc25c72df11095 Mon Sep 17 00:00:00 2001 From: Muhammad Azeez Date: Mon, 2 Dec 2024 21:00:07 +0300 Subject: [PATCH 4/9] formatting --- src/Extism.Pdk.MSBuild/FFIGenerator.cs | 399 ++++++++++++------------- src/Extism.Pdk/Interop.cs | 10 +- 2 files changed, 204 insertions(+), 205 deletions(-) diff --git a/src/Extism.Pdk.MSBuild/FFIGenerator.cs b/src/Extism.Pdk.MSBuild/FFIGenerator.cs index 7492fbc..d022f09 100644 --- a/src/Extism.Pdk.MSBuild/FFIGenerator.cs +++ b/src/Extism.Pdk.MSBuild/FFIGenerator.cs @@ -2,182 +2,182 @@ using System.Text; -namespace Extism.Pdk.MSBuild +namespace Extism.Pdk.MSBuild; + +/// +/// Generate the necessary glue code to export/import .NET functions +/// +public class FFIGenerator { + private readonly Action _logError; + private readonly string _extism; + /// - /// Generate the necessary glue code to export/import .NET functions + /// Constructor /// - public class FFIGenerator + /// + /// + public FFIGenerator(string extism, Action logError) { - private readonly Action _logError; - private readonly string _extism; - - /// - /// Constructor - /// - /// - /// - public FFIGenerator(string extism, Action logError) - { - _logError = logError; - _extism = extism; - } - - /// - /// Generate glue code for the given assembly and referenced assemblies - /// - /// - /// - /// - public IEnumerable GenerateGlueCode(AssemblyDefinition assembly, string directory) - { - var assemblies = assembly.MainModule.AssemblyReferences - .Where(r => !r.Name.StartsWith("System") && !r.Name.StartsWith("Microsoft") && r.Name != "Extism.Pdk") - .Select(r => AssemblyDefinition.ReadAssembly(Path.Combine(directory, r.Name + ".dll"))) - .ToList(); + _logError = logError; + _extism = extism; + } - assemblies.Add(assembly); + /// + /// Generate glue code for the given assembly and referenced assemblies + /// + /// + /// + /// + public IEnumerable GenerateGlueCode(AssemblyDefinition assembly, string directory) + { + var assemblies = assembly.MainModule.AssemblyReferences + .Where(r => !r.Name.StartsWith("System") && !r.Name.StartsWith("Microsoft") && r.Name != "Extism.Pdk") + .Select(r => AssemblyDefinition.ReadAssembly(Path.Combine(directory, r.Name + ".dll"))) + .ToList(); - var types = assemblies.SelectMany(a => a.MainModule.Types).ToArray(); + assemblies.Add(assembly); - var exportedMethods = types - .SelectMany(t => t.Methods) - .Where(m => m.IsStatic && m.CustomAttributes.Any(a => a.AttributeType.FullName == "System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute")) - .ToArray(); + var types = assemblies.SelectMany(a => a.MainModule.Types).ToArray(); - // TODO: also find F# module functions - var importedMethods = types + var exportedMethods = types .SelectMany(t => t.Methods) - .Where(m => m.HasPInvokeInfo) - .ToArray(); + .Where(m => m.IsStatic && m.CustomAttributes.Any(a => a.AttributeType.FullName == "System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute")) + .ToArray(); - var files = GenerateImports(importedMethods, _extism); - files.Add(GenerateExports(exportedMethods)); + // TODO: also find F# module functions + var importedMethods = types + .SelectMany(t => t.Methods) + .Where(m => m.HasPInvokeInfo) + .ToArray(); - return files; - } + var files = GenerateImports(importedMethods, _extism); + files.Add(GenerateExports(exportedMethods)); - private List GenerateImports(MethodDefinition[] importedMethods, string extism) - { - var modules = importedMethods.GroupBy(m => m.PInvokeInfo.Module.Name) - .Select(g => new - { - Name = g.Key, - Imports = g.Select(m => ToImportStatement(m)).ToArray(), - }) - .ToList(); + return files; + } - var extismWritten = false; + private List GenerateImports(MethodDefinition[] importedMethods, string extism) + { + var modules = importedMethods.GroupBy(m => m.PInvokeInfo.Module.Name) + .Select(g => new + { + Name = g.Key, + Imports = g.Select(m => ToImportStatement(m)).ToArray(), + }) + .ToList(); - var files = new List(); + var extismWritten = false; - // For DllImport to work with wasm, the name of the file has to match - // the name of the module that the function is imported from - foreach (var module in modules) - { - var builder = new StringBuilder(); + var files = new List(); - if (module.Name == "extism") - { - extismWritten = true; - builder.AppendLine(extism); - } - else - { - builder.AppendLine(Preamble); - } - - foreach (var import in module.Imports) - { - builder.AppendLine(import); - } + // For DllImport to work with wasm, the name of the file has to match + // the name of the module that the function is imported from + foreach (var module in modules) + { + var builder = new StringBuilder(); - files.Add(new FileEntry { Name = $"{module.Name}.c", Content = builder.ToString() }); + if (module.Name == "extism") + { + extismWritten = true; + builder.AppendLine(extism); + } + else + { + builder.AppendLine(Preamble); } - if (!extismWritten) + foreach (var import in module.Imports) { - files.Add(new FileEntry { Name = $"extism.c", Content = extism }); + builder.AppendLine(import); } - return files; + files.Add(new FileEntry { Name = $"{module.Name}.c", Content = builder.ToString() }); } - private FileEntry GenerateExports(MethodDefinition[] exportedMethods) + if (!extismWritten) { - var sb = new StringBuilder(); + files.Add(new FileEntry { Name = $"extism.c", Content = extism }); + } - if (exportedMethods.Length > 0) - { - sb.AppendLine(Preamble); - sb.AppendLine( - """ - // _initialize - void mono_wasm_load_runtime(const char* unused, int debug_level); - - #ifdef WASI_AFTER_RUNTIME_LOADED_DECLARATIONS - // This is supplied from the MSBuild itemgroup @(WasiAfterRuntimeLoaded) - WASI_AFTER_RUNTIME_LOADED_DECLARATIONS - #endif - - void initialize_runtime() { - mono_wasm_load_runtime("", 0); - } + return files; + } - // end of _initialize - """); + private FileEntry GenerateExports(MethodDefinition[] exportedMethods) + { + var sb = new StringBuilder(); - sb.AppendLine( - """ + if (exportedMethods.Length > 0) + { + sb.AppendLine(Preamble); + sb.AppendLine( + """ + // _initialize + void mono_wasm_load_runtime(const char* unused, int debug_level); + + #ifdef WASI_AFTER_RUNTIME_LOADED_DECLARATIONS + // This is supplied from the MSBuild itemgroup @(WasiAfterRuntimeLoaded) + WASI_AFTER_RUNTIME_LOADED_DECLARATIONS + #endif + + void initialize_runtime() { + mono_wasm_load_runtime("", 0); + } - void mono_wasm_invoke_method_ref(MonoMethod* method, MonoObject** this_arg_in, void* params[], MonoObject** _out_exc, MonoObject** out_result); - MonoString* mono_object_try_to_string (MonoObject *obj, MonoObject **exc, MonoError *error); - void mono_print_unhandled_exception(MonoObject *exc); + // end of _initialize + """); - MonoMethod* method_extism_print_exception; - void extism_print_exception(MonoObject* exc) - { - if (!method_extism_print_exception) - { - method_extism_print_exception = lookup_dotnet_method("Extism.Pdk.dll", "Extism", "Native", "PrintException", -1); + sb.AppendLine( + """ - if (method_extism_print_exception == NULL) { - printf("Fatal: Failed to find Extism.Native.PrintException"); - } + void mono_wasm_invoke_method_ref(MonoMethod* method, MonoObject** this_arg_in, void* params[], MonoObject** _out_exc, MonoObject** out_result); + MonoString* mono_object_try_to_string (MonoObject *obj, MonoObject **exc, MonoError *error); + void mono_print_unhandled_exception(MonoObject *exc); - assert(method_extism_print_exception); - } + MonoMethod* method_extism_print_exception; + void extism_print_exception(MonoObject* exc) + { + if (!method_extism_print_exception) + { + method_extism_print_exception = lookup_dotnet_method("Extism.Pdk.dll", "Extism", "Native", "PrintException", -1); - void* method_params[] = { exc }; - MonoObject* exception = NULL; - MonoObject* result = NULL; - mono_wasm_invoke_method_ref(method_extism_print_exception, NULL, method_params, &exception, &result); - - if (exception != NULL) { - const char* message = "An exception was thrown while trying to print the previous exception. Please check stderr for details."; - mono_print_unhandled_exception(exception); + if (method_extism_print_exception == NULL) { + printf("Fatal: Failed to find Extism.Native.PrintException"); } + + assert(method_extism_print_exception); } - """); + void* method_params[] = { exc }; + MonoObject* exception = NULL; + MonoObject* result = NULL; + mono_wasm_invoke_method_ref(method_extism_print_exception, NULL, method_params, &exception, &result); + + if (exception != NULL) { + const char* message = "An exception was thrown while trying to print the previous exception. Please check stderr for details."; + mono_print_unhandled_exception(exception); + } + } + + """); - foreach (var method in exportedMethods) + foreach (var method in exportedMethods) + { + if (method.Parameters.Count > 0) { - if (method.Parameters.Count > 0) - { - var parameterNames = string.Join(",", method.Parameters.Select(p => $"{p.ParameterType.Name} {p.Name}")); - _logError($"Extism doesn't support exporting functions that have parameters: {method.DeclaringType.FullName}.{method.Name}({parameterNames})"); - } + var parameterNames = string.Join(",", method.Parameters.Select(p => $"{p.ParameterType.Name} {p.Name}")); + _logError($"Extism doesn't support exporting functions that have parameters: {method.DeclaringType.FullName}.{method.Name}({parameterNames})"); + } - var assemblyFileName = method.Module.Assembly.Name.Name + ".dll"; - var attribute = method.CustomAttributes.First(a => a.AttributeType.Name == "UnmanagedCallersOnlyAttribute"); + var assemblyFileName = method.Module.Assembly.Name.Name + ".dll"; + var attribute = method.CustomAttributes.First(a => a.AttributeType.Name == "UnmanagedCallersOnlyAttribute"); - var exportName = attribute.Fields.FirstOrDefault(p => p.Name == "EntryPoint").Argument.Value?.ToString() ?? method.Name; - var parameterCount = method.Parameters.Count; - var methodParams = string.Join(", ", Enumerable.Repeat("NULL", parameterCount)); - var returnType = method.ReturnType.FullName; + var exportName = attribute.Fields.FirstOrDefault(p => p.Name == "EntryPoint").Argument.Value?.ToString() ?? method.Name; + var parameterCount = method.Parameters.Count; + var methodParams = string.Join(", ", Enumerable.Repeat("NULL", parameterCount)); + var returnType = method.ReturnType.FullName; - sb.AppendLine($$""" + sb.AppendLine($$""" MonoMethod* method_{{exportName}}; __attribute__((export_name("{{exportName}}"))) int {{exportName}}() { @@ -211,56 +211,56 @@ void extism_print_exception(MonoObject* exc) return int_result; } """); - } } - - sb.AppendLine(); - return new FileEntry { Name = "exports.c", Content = sb.ToString() }; } - private string ToImportStatement(MethodDefinition method) + sb.AppendLine(); + return new FileEntry { Name = "exports.c", Content = sb.ToString() }; + } + + private string ToImportStatement(MethodDefinition method) + { + var moduleName = method.PInvokeInfo.Module.Name; + if (moduleName == "extism") { - var moduleName = method.PInvokeInfo.Module.Name; - if (moduleName == "extism") - { - // Redirect imported host functions to extism:host/user - // The PDK functions don't use this generator, so we can safely assume - // every `extism` import is a user host function - moduleName = "extism:host/user"; - } + // Redirect imported host functions to extism:host/user + // The PDK functions don't use this generator, so we can safely assume + // every `extism` import is a user host function + moduleName = "extism:host/user"; + } - var functionName = string.IsNullOrEmpty(method.PInvokeInfo.EntryPoint) ? method.Name : method.PInvokeInfo.EntryPoint; + var functionName = string.IsNullOrEmpty(method.PInvokeInfo.EntryPoint) ? method.Name : method.PInvokeInfo.EntryPoint; - if (!_types.ContainsKey(method.ReturnType.Name)) - { - _logError($"Unsupported return type: {method.ReturnType.FullName} on {method.FullName} method."); - return ""; - } + if (!_types.ContainsKey(method.ReturnType.Name)) + { + _logError($"Unsupported return type: {method.ReturnType.FullName} on {method.FullName} method."); + return ""; + } - var sb = new StringBuilder(); - var p = method.Parameters.FirstOrDefault(p => !_types.ContainsKey(p.ParameterType.Name)); - if (p != null) - { - _logError($"Unsupported parameter type: {p.Name} ({p.ParameterType.FullName}) on {method.FullName} method."); + var sb = new StringBuilder(); + var p = method.Parameters.FirstOrDefault(p => !_types.ContainsKey(p.ParameterType.Name)); + if (p != null) + { + _logError($"Unsupported parameter type: {p.Name} ({p.ParameterType.FullName}) on {method.FullName} method."); - return $"\\\\ Unrecognized type: ${p.ParameterType.Name} => '{p.ParameterType.FullName}'."; - } + return $"\\\\ Unrecognized type: ${p.ParameterType.Name} => '{p.ParameterType.FullName}'."; + } - var parameters = string.Join(", ", method.Parameters.Select(p => $"{_types[p.ParameterType.Name]} {p.Name}")); - var parameterNames = string.Join(", ", method.Parameters.Select(p => p.Name)); + var parameters = string.Join(", ", method.Parameters.Select(p => $"{_types[p.ParameterType.Name]} {p.Name}")); + var parameterNames = string.Join(", ", method.Parameters.Select(p => p.Name)); - var returnKeyword = _types[method.ReturnType.Name] == "void" ? "" : "return "; + var returnKeyword = _types[method.ReturnType.Name] == "void" ? "" : "return "; - return $$""" - IMPORT("{{moduleName}}", "{{functionName}}") extern {{_types[method.ReturnType.Name]}} {{functionName}}_import({{parameters}}); - - {{_types[method.ReturnType.Name]}} {{functionName}}({{parameters}}) { - {{returnKeyword}}{{functionName}}_import({{parameterNames}}); - } - """; + return $$""" + IMPORT("{{moduleName}}", "{{functionName}}") extern {{_types[method.ReturnType.Name]}} {{functionName}}_import({{parameters}}); + + {{_types[method.ReturnType.Name]}} {{functionName}}({{parameters}}) { + {{returnKeyword}}{{functionName}}_import({{parameterNames}}); } + """; + } - private const string Preamble = """ + private const string Preamble = """ #include #include #include @@ -281,38 +281,37 @@ private string ToImportStatement(MethodDefinition method) typedef uint64_t ExtismPointer; """; - private static readonly Dictionary _types = new Dictionary - { - { nameof(SByte), "int8_t" }, - { nameof(Int16), "int16_t" }, - { nameof(Int32), "int32_t" }, - { nameof(Int64), "int64_t" }, + private static readonly Dictionary _types = new Dictionary + { + { nameof(SByte), "int8_t" }, + { nameof(Int16), "int16_t" }, + { nameof(Int32), "int32_t" }, + { nameof(Int64), "int64_t" }, - { nameof(Byte), "uint8_t" }, - { nameof(UInt16), "uint16_t" }, - { nameof(UInt32), "uint32_t" }, - { nameof(UInt64), "uint64_t" }, + { nameof(Byte), "uint8_t" }, + { nameof(UInt16), "uint16_t" }, + { nameof(UInt32), "uint32_t" }, + { nameof(UInt64), "uint64_t" }, - { nameof(Single), "float" }, - { nameof(Double), "double" }, + { nameof(Single), "float" }, + { nameof(Double), "double" }, - { "Void", "void"}, - }; - } + { "Void", "void"}, + }; +} +/// +/// A file generated by the task +/// +public class FileEntry +{ /// - /// A file generated by the task + /// Name of the file /// - public class FileEntry - { - /// - /// Name of the file - /// - public string Name { get; set; } = default!; - - /// - /// Content of the file - /// - public string Content { get; set; } = default!; - } + public string Name { get; set; } = default!; + + /// + /// Content of the file + /// + public string Content { get; set; } = default!; } \ No newline at end of file diff --git a/src/Extism.Pdk/Interop.cs b/src/Extism.Pdk/Interop.cs index 8d22434..eab17b0 100644 --- a/src/Extism.Pdk/Interop.cs +++ b/src/Extism.Pdk/Interop.cs @@ -361,27 +361,27 @@ public static HttpResponse SendRequest(HttpRequest request) public enum LogLevel { /// - /// Trace level logging + /// Trace /// Trace = 0, /// - /// Debug level logging + /// Debug /// Debug = 1, /// - /// Information level logging + /// Information /// Info = 2, /// - /// Warning level logging + /// Warning /// Warn = 3, /// - /// Error level logging + /// Error /// Error = 4, } From d33b807307c7f9aca61488138664601086cfd334 Mon Sep 17 00:00:00 2001 From: Muhammad Azeez Date: Mon, 2 Dec 2024 21:02:04 +0300 Subject: [PATCH 5/9] remove unsafe --- src/Extism.Pdk/Interop.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Extism.Pdk/Interop.cs b/src/Extism.Pdk/Interop.cs index eab17b0..443d8e1 100644 --- a/src/Extism.Pdk/Interop.cs +++ b/src/Extism.Pdk/Interop.cs @@ -17,7 +17,7 @@ public static class Pdk /// Read the input data sent by the host. /// /// The input data as a byte array. - public static unsafe byte[] GetInput() + public static byte[] GetInput() { var length = Native.extism_input_length(); if (length == 0) @@ -122,7 +122,7 @@ public static void SetOutputJson(T output, JsonTypeInfo typeInfo) /// Set plugin error /// /// - public static unsafe void SetError(string errorMessage) + public static void SetError(string errorMessage) { var block = Allocate(errorMessage); Native.extism_error_set(block.Offset); From 3718381283acff668aa4064104666ab7b3a3b88b Mon Sep 17 00:00:00 2001 From: Muhammad Azeez Date: Mon, 2 Dec 2024 21:03:44 +0300 Subject: [PATCH 6/9] format --- src/Extism.Pdk.MSBuild/FFIGenerator.cs | 399 +++++++++++++------------ 1 file changed, 200 insertions(+), 199 deletions(-) diff --git a/src/Extism.Pdk.MSBuild/FFIGenerator.cs b/src/Extism.Pdk.MSBuild/FFIGenerator.cs index d022f09..7492fbc 100644 --- a/src/Extism.Pdk.MSBuild/FFIGenerator.cs +++ b/src/Extism.Pdk.MSBuild/FFIGenerator.cs @@ -2,182 +2,182 @@ using System.Text; -namespace Extism.Pdk.MSBuild; - -/// -/// Generate the necessary glue code to export/import .NET functions -/// -public class FFIGenerator +namespace Extism.Pdk.MSBuild { - private readonly Action _logError; - private readonly string _extism; - /// - /// Constructor + /// Generate the necessary glue code to export/import .NET functions /// - /// - /// - public FFIGenerator(string extism, Action logError) + public class FFIGenerator { - _logError = logError; - _extism = extism; - } + private readonly Action _logError; + private readonly string _extism; + + /// + /// Constructor + /// + /// + /// + public FFIGenerator(string extism, Action logError) + { + _logError = logError; + _extism = extism; + } - /// - /// Generate glue code for the given assembly and referenced assemblies - /// - /// - /// - /// - public IEnumerable GenerateGlueCode(AssemblyDefinition assembly, string directory) - { - var assemblies = assembly.MainModule.AssemblyReferences - .Where(r => !r.Name.StartsWith("System") && !r.Name.StartsWith("Microsoft") && r.Name != "Extism.Pdk") - .Select(r => AssemblyDefinition.ReadAssembly(Path.Combine(directory, r.Name + ".dll"))) - .ToList(); + /// + /// Generate glue code for the given assembly and referenced assemblies + /// + /// + /// + /// + public IEnumerable GenerateGlueCode(AssemblyDefinition assembly, string directory) + { + var assemblies = assembly.MainModule.AssemblyReferences + .Where(r => !r.Name.StartsWith("System") && !r.Name.StartsWith("Microsoft") && r.Name != "Extism.Pdk") + .Select(r => AssemblyDefinition.ReadAssembly(Path.Combine(directory, r.Name + ".dll"))) + .ToList(); + + assemblies.Add(assembly); - assemblies.Add(assembly); + var types = assemblies.SelectMany(a => a.MainModule.Types).ToArray(); - var types = assemblies.SelectMany(a => a.MainModule.Types).ToArray(); + var exportedMethods = types + .SelectMany(t => t.Methods) + .Where(m => m.IsStatic && m.CustomAttributes.Any(a => a.AttributeType.FullName == "System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute")) + .ToArray(); - var exportedMethods = types + // TODO: also find F# module functions + var importedMethods = types .SelectMany(t => t.Methods) - .Where(m => m.IsStatic && m.CustomAttributes.Any(a => a.AttributeType.FullName == "System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute")) - .ToArray(); + .Where(m => m.HasPInvokeInfo) + .ToArray(); - // TODO: also find F# module functions - var importedMethods = types - .SelectMany(t => t.Methods) - .Where(m => m.HasPInvokeInfo) - .ToArray(); + var files = GenerateImports(importedMethods, _extism); + files.Add(GenerateExports(exportedMethods)); - var files = GenerateImports(importedMethods, _extism); - files.Add(GenerateExports(exportedMethods)); + return files; + } - return files; - } + private List GenerateImports(MethodDefinition[] importedMethods, string extism) + { + var modules = importedMethods.GroupBy(m => m.PInvokeInfo.Module.Name) + .Select(g => new + { + Name = g.Key, + Imports = g.Select(m => ToImportStatement(m)).ToArray(), + }) + .ToList(); - private List GenerateImports(MethodDefinition[] importedMethods, string extism) - { - var modules = importedMethods.GroupBy(m => m.PInvokeInfo.Module.Name) - .Select(g => new - { - Name = g.Key, - Imports = g.Select(m => ToImportStatement(m)).ToArray(), - }) - .ToList(); + var extismWritten = false; - var extismWritten = false; + var files = new List(); - var files = new List(); + // For DllImport to work with wasm, the name of the file has to match + // the name of the module that the function is imported from + foreach (var module in modules) + { + var builder = new StringBuilder(); - // For DllImport to work with wasm, the name of the file has to match - // the name of the module that the function is imported from - foreach (var module in modules) - { - var builder = new StringBuilder(); + if (module.Name == "extism") + { + extismWritten = true; + builder.AppendLine(extism); + } + else + { + builder.AppendLine(Preamble); + } - if (module.Name == "extism") - { - extismWritten = true; - builder.AppendLine(extism); - } - else - { - builder.AppendLine(Preamble); + foreach (var import in module.Imports) + { + builder.AppendLine(import); + } + + files.Add(new FileEntry { Name = $"{module.Name}.c", Content = builder.ToString() }); } - foreach (var import in module.Imports) + if (!extismWritten) { - builder.AppendLine(import); + files.Add(new FileEntry { Name = $"extism.c", Content = extism }); } - files.Add(new FileEntry { Name = $"{module.Name}.c", Content = builder.ToString() }); + return files; } - if (!extismWritten) + private FileEntry GenerateExports(MethodDefinition[] exportedMethods) { - files.Add(new FileEntry { Name = $"extism.c", Content = extism }); - } - - return files; - } - - private FileEntry GenerateExports(MethodDefinition[] exportedMethods) - { - var sb = new StringBuilder(); + var sb = new StringBuilder(); - if (exportedMethods.Length > 0) - { - sb.AppendLine(Preamble); - sb.AppendLine( - """ - // _initialize - void mono_wasm_load_runtime(const char* unused, int debug_level); - - #ifdef WASI_AFTER_RUNTIME_LOADED_DECLARATIONS - // This is supplied from the MSBuild itemgroup @(WasiAfterRuntimeLoaded) - WASI_AFTER_RUNTIME_LOADED_DECLARATIONS - #endif - - void initialize_runtime() { - mono_wasm_load_runtime("", 0); - } + if (exportedMethods.Length > 0) + { + sb.AppendLine(Preamble); + sb.AppendLine( + """ + // _initialize + void mono_wasm_load_runtime(const char* unused, int debug_level); + + #ifdef WASI_AFTER_RUNTIME_LOADED_DECLARATIONS + // This is supplied from the MSBuild itemgroup @(WasiAfterRuntimeLoaded) + WASI_AFTER_RUNTIME_LOADED_DECLARATIONS + #endif + + void initialize_runtime() { + mono_wasm_load_runtime("", 0); + } - // end of _initialize - """); + // end of _initialize + """); - sb.AppendLine( - """ + sb.AppendLine( + """ - void mono_wasm_invoke_method_ref(MonoMethod* method, MonoObject** this_arg_in, void* params[], MonoObject** _out_exc, MonoObject** out_result); - MonoString* mono_object_try_to_string (MonoObject *obj, MonoObject **exc, MonoError *error); - void mono_print_unhandled_exception(MonoObject *exc); + void mono_wasm_invoke_method_ref(MonoMethod* method, MonoObject** this_arg_in, void* params[], MonoObject** _out_exc, MonoObject** out_result); + MonoString* mono_object_try_to_string (MonoObject *obj, MonoObject **exc, MonoError *error); + void mono_print_unhandled_exception(MonoObject *exc); - MonoMethod* method_extism_print_exception; - void extism_print_exception(MonoObject* exc) - { - if (!method_extism_print_exception) + MonoMethod* method_extism_print_exception; + void extism_print_exception(MonoObject* exc) { - method_extism_print_exception = lookup_dotnet_method("Extism.Pdk.dll", "Extism", "Native", "PrintException", -1); + if (!method_extism_print_exception) + { + method_extism_print_exception = lookup_dotnet_method("Extism.Pdk.dll", "Extism", "Native", "PrintException", -1); - if (method_extism_print_exception == NULL) { - printf("Fatal: Failed to find Extism.Native.PrintException"); - } + if (method_extism_print_exception == NULL) { + printf("Fatal: Failed to find Extism.Native.PrintException"); + } - assert(method_extism_print_exception); - } + assert(method_extism_print_exception); + } - void* method_params[] = { exc }; - MonoObject* exception = NULL; - MonoObject* result = NULL; - mono_wasm_invoke_method_ref(method_extism_print_exception, NULL, method_params, &exception, &result); - - if (exception != NULL) { - const char* message = "An exception was thrown while trying to print the previous exception. Please check stderr for details."; - mono_print_unhandled_exception(exception); + void* method_params[] = { exc }; + MonoObject* exception = NULL; + MonoObject* result = NULL; + mono_wasm_invoke_method_ref(method_extism_print_exception, NULL, method_params, &exception, &result); + + if (exception != NULL) { + const char* message = "An exception was thrown while trying to print the previous exception. Please check stderr for details."; + mono_print_unhandled_exception(exception); + } } - } - """); + """); - foreach (var method in exportedMethods) - { - if (method.Parameters.Count > 0) + foreach (var method in exportedMethods) { - var parameterNames = string.Join(",", method.Parameters.Select(p => $"{p.ParameterType.Name} {p.Name}")); - _logError($"Extism doesn't support exporting functions that have parameters: {method.DeclaringType.FullName}.{method.Name}({parameterNames})"); - } + if (method.Parameters.Count > 0) + { + var parameterNames = string.Join(",", method.Parameters.Select(p => $"{p.ParameterType.Name} {p.Name}")); + _logError($"Extism doesn't support exporting functions that have parameters: {method.DeclaringType.FullName}.{method.Name}({parameterNames})"); + } - var assemblyFileName = method.Module.Assembly.Name.Name + ".dll"; - var attribute = method.CustomAttributes.First(a => a.AttributeType.Name == "UnmanagedCallersOnlyAttribute"); + var assemblyFileName = method.Module.Assembly.Name.Name + ".dll"; + var attribute = method.CustomAttributes.First(a => a.AttributeType.Name == "UnmanagedCallersOnlyAttribute"); - var exportName = attribute.Fields.FirstOrDefault(p => p.Name == "EntryPoint").Argument.Value?.ToString() ?? method.Name; - var parameterCount = method.Parameters.Count; - var methodParams = string.Join(", ", Enumerable.Repeat("NULL", parameterCount)); - var returnType = method.ReturnType.FullName; + var exportName = attribute.Fields.FirstOrDefault(p => p.Name == "EntryPoint").Argument.Value?.ToString() ?? method.Name; + var parameterCount = method.Parameters.Count; + var methodParams = string.Join(", ", Enumerable.Repeat("NULL", parameterCount)); + var returnType = method.ReturnType.FullName; - sb.AppendLine($$""" + sb.AppendLine($$""" MonoMethod* method_{{exportName}}; __attribute__((export_name("{{exportName}}"))) int {{exportName}}() { @@ -211,56 +211,56 @@ void extism_print_exception(MonoObject* exc) return int_result; } """); + } } - } - sb.AppendLine(); - return new FileEntry { Name = "exports.c", Content = sb.ToString() }; - } + sb.AppendLine(); + return new FileEntry { Name = "exports.c", Content = sb.ToString() }; + } - private string ToImportStatement(MethodDefinition method) - { - var moduleName = method.PInvokeInfo.Module.Name; - if (moduleName == "extism") + private string ToImportStatement(MethodDefinition method) { - // Redirect imported host functions to extism:host/user - // The PDK functions don't use this generator, so we can safely assume - // every `extism` import is a user host function - moduleName = "extism:host/user"; - } + var moduleName = method.PInvokeInfo.Module.Name; + if (moduleName == "extism") + { + // Redirect imported host functions to extism:host/user + // The PDK functions don't use this generator, so we can safely assume + // every `extism` import is a user host function + moduleName = "extism:host/user"; + } - var functionName = string.IsNullOrEmpty(method.PInvokeInfo.EntryPoint) ? method.Name : method.PInvokeInfo.EntryPoint; + var functionName = string.IsNullOrEmpty(method.PInvokeInfo.EntryPoint) ? method.Name : method.PInvokeInfo.EntryPoint; - if (!_types.ContainsKey(method.ReturnType.Name)) - { - _logError($"Unsupported return type: {method.ReturnType.FullName} on {method.FullName} method."); - return ""; - } + if (!_types.ContainsKey(method.ReturnType.Name)) + { + _logError($"Unsupported return type: {method.ReturnType.FullName} on {method.FullName} method."); + return ""; + } - var sb = new StringBuilder(); - var p = method.Parameters.FirstOrDefault(p => !_types.ContainsKey(p.ParameterType.Name)); - if (p != null) - { - _logError($"Unsupported parameter type: {p.Name} ({p.ParameterType.FullName}) on {method.FullName} method."); + var sb = new StringBuilder(); + var p = method.Parameters.FirstOrDefault(p => !_types.ContainsKey(p.ParameterType.Name)); + if (p != null) + { + _logError($"Unsupported parameter type: {p.Name} ({p.ParameterType.FullName}) on {method.FullName} method."); - return $"\\\\ Unrecognized type: ${p.ParameterType.Name} => '{p.ParameterType.FullName}'."; - } + return $"\\\\ Unrecognized type: ${p.ParameterType.Name} => '{p.ParameterType.FullName}'."; + } - var parameters = string.Join(", ", method.Parameters.Select(p => $"{_types[p.ParameterType.Name]} {p.Name}")); - var parameterNames = string.Join(", ", method.Parameters.Select(p => p.Name)); + var parameters = string.Join(", ", method.Parameters.Select(p => $"{_types[p.ParameterType.Name]} {p.Name}")); + var parameterNames = string.Join(", ", method.Parameters.Select(p => p.Name)); - var returnKeyword = _types[method.ReturnType.Name] == "void" ? "" : "return "; + var returnKeyword = _types[method.ReturnType.Name] == "void" ? "" : "return "; - return $$""" - IMPORT("{{moduleName}}", "{{functionName}}") extern {{_types[method.ReturnType.Name]}} {{functionName}}_import({{parameters}}); - - {{_types[method.ReturnType.Name]}} {{functionName}}({{parameters}}) { - {{returnKeyword}}{{functionName}}_import({{parameterNames}}); + return $$""" + IMPORT("{{moduleName}}", "{{functionName}}") extern {{_types[method.ReturnType.Name]}} {{functionName}}_import({{parameters}}); + + {{_types[method.ReturnType.Name]}} {{functionName}}({{parameters}}) { + {{returnKeyword}}{{functionName}}_import({{parameterNames}}); + } + """; } - """; - } - private const string Preamble = """ + private const string Preamble = """ #include #include #include @@ -281,37 +281,38 @@ private string ToImportStatement(MethodDefinition method) typedef uint64_t ExtismPointer; """; - private static readonly Dictionary _types = new Dictionary - { - { nameof(SByte), "int8_t" }, - { nameof(Int16), "int16_t" }, - { nameof(Int32), "int32_t" }, - { nameof(Int64), "int64_t" }, - - { nameof(Byte), "uint8_t" }, - { nameof(UInt16), "uint16_t" }, - { nameof(UInt32), "uint32_t" }, - { nameof(UInt64), "uint64_t" }, + private static readonly Dictionary _types = new Dictionary + { + { nameof(SByte), "int8_t" }, + { nameof(Int16), "int16_t" }, + { nameof(Int32), "int32_t" }, + { nameof(Int64), "int64_t" }, - { nameof(Single), "float" }, - { nameof(Double), "double" }, + { nameof(Byte), "uint8_t" }, + { nameof(UInt16), "uint16_t" }, + { nameof(UInt32), "uint32_t" }, + { nameof(UInt64), "uint64_t" }, - { "Void", "void"}, - }; -} + { nameof(Single), "float" }, + { nameof(Double), "double" }, -/// -/// A file generated by the task -/// -public class FileEntry -{ - /// - /// Name of the file - /// - public string Name { get; set; } = default!; + { "Void", "void"}, + }; + } /// - /// Content of the file + /// A file generated by the task /// - public string Content { get; set; } = default!; + public class FileEntry + { + /// + /// Name of the file + /// + public string Name { get; set; } = default!; + + /// + /// Content of the file + /// + public string Content { get; set; } = default!; + } } \ No newline at end of file From bf8b4be7bcfe8d559e47d02dc2be1a2aedece52a Mon Sep 17 00:00:00 2001 From: Muhammad Azeez Date: Thu, 5 Dec 2024 15:57:30 +0300 Subject: [PATCH 7/9] update nuget packages --- samples/KitchenSink/KitchenSink.csproj | 4 ++++ samples/SampleCSharpPlugin/SampleCSharpPlugin.csproj | 4 ++++ samples/SampleFSharpPlugin/SampleFSharpPlugin.fsproj | 8 ++++++++ samples/SampleHost/SampleHost.csproj | 4 ++-- samples/SampleLib/SampleLib.csproj | 5 +++++ src/Extism.Pdk.MSBuild/Extism.Pdk.MSBuild.csproj | 4 ++++ src/Extism.Pdk/Extism.Pdk.csproj | 2 ++ .../Extism.Pdk.MsBuild.Tests.csproj | 6 ++++-- tests/Extism.Pdk.WasmTests/Extism.Pdk.WasmTests.csproj | 9 +++++---- 9 files changed, 38 insertions(+), 8 deletions(-) diff --git a/samples/KitchenSink/KitchenSink.csproj b/samples/KitchenSink/KitchenSink.csproj index 939f558..e830cef 100644 --- a/samples/KitchenSink/KitchenSink.csproj +++ b/samples/KitchenSink/KitchenSink.csproj @@ -9,6 +9,10 @@ false + + + + diff --git a/samples/SampleCSharpPlugin/SampleCSharpPlugin.csproj b/samples/SampleCSharpPlugin/SampleCSharpPlugin.csproj index 4dbe593..9b22165 100644 --- a/samples/SampleCSharpPlugin/SampleCSharpPlugin.csproj +++ b/samples/SampleCSharpPlugin/SampleCSharpPlugin.csproj @@ -7,6 +7,10 @@ true + + + + diff --git a/samples/SampleFSharpPlugin/SampleFSharpPlugin.fsproj b/samples/SampleFSharpPlugin/SampleFSharpPlugin.fsproj index 4021e9d..05d0f67 100644 --- a/samples/SampleFSharpPlugin/SampleFSharpPlugin.fsproj +++ b/samples/SampleFSharpPlugin/SampleFSharpPlugin.fsproj @@ -12,9 +12,17 @@ + + + + + + + + diff --git a/samples/SampleHost/SampleHost.csproj b/samples/SampleHost/SampleHost.csproj index a454956..deee25a 100644 --- a/samples/SampleHost/SampleHost.csproj +++ b/samples/SampleHost/SampleHost.csproj @@ -8,8 +8,8 @@ - - + + diff --git a/samples/SampleLib/SampleLib.csproj b/samples/SampleLib/SampleLib.csproj index 70ab331..bdd1f13 100644 --- a/samples/SampleLib/SampleLib.csproj +++ b/samples/SampleLib/SampleLib.csproj @@ -6,6 +6,11 @@ enable + + + + + diff --git a/src/Extism.Pdk.MSBuild/Extism.Pdk.MSBuild.csproj b/src/Extism.Pdk.MSBuild/Extism.Pdk.MSBuild.csproj index 5624199..378d826 100644 --- a/src/Extism.Pdk.MSBuild/Extism.Pdk.MSBuild.csproj +++ b/src/Extism.Pdk.MSBuild/Extism.Pdk.MSBuild.csproj @@ -17,6 +17,10 @@ + + + + diff --git a/src/Extism.Pdk/Extism.Pdk.csproj b/src/Extism.Pdk/Extism.Pdk.csproj index afb55c0..dcb46e3 100644 --- a/src/Extism.Pdk/Extism.Pdk.csproj +++ b/src/Extism.Pdk/Extism.Pdk.csproj @@ -22,6 +22,8 @@ + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/Extism.Pdk.MsBuild.Tests/Extism.Pdk.MsBuild.Tests.csproj b/tests/Extism.Pdk.MsBuild.Tests/Extism.Pdk.MsBuild.Tests.csproj index 59e436f..66ac876 100644 --- a/tests/Extism.Pdk.MsBuild.Tests/Extism.Pdk.MsBuild.Tests.csproj +++ b/tests/Extism.Pdk.MsBuild.Tests/Extism.Pdk.MsBuild.Tests.csproj @@ -10,11 +10,13 @@ - + + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all diff --git a/tests/Extism.Pdk.WasmTests/Extism.Pdk.WasmTests.csproj b/tests/Extism.Pdk.WasmTests/Extism.Pdk.WasmTests.csproj index 13a105a..d8509d9 100644 --- a/tests/Extism.Pdk.WasmTests/Extism.Pdk.WasmTests.csproj +++ b/tests/Extism.Pdk.WasmTests/Extism.Pdk.WasmTests.csproj @@ -9,12 +9,13 @@ - - - + + + + - + runtime; build; native; contentfiles; analyzers; buildtransitive all From 8519836bde64c968243f9e9334f1d2126ad0c1ca Mon Sep 17 00:00:00 2001 From: Muhammad Azeez Date: Thu, 5 Dec 2024 17:17:35 +0300 Subject: [PATCH 8/9] Fix build error --- src/Extism.Pdk/Interop.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Extism.Pdk/Interop.cs b/src/Extism.Pdk/Interop.cs index 443d8e1..51d5e0b 100644 --- a/src/Extism.Pdk/Interop.cs +++ b/src/Extism.Pdk/Interop.cs @@ -431,7 +431,7 @@ public HttpRequest(string url) /// /// An optional body. /// - [JsonPropertyName("body")] + [JsonIgnore] public byte[] Body { get; set; } = Array.Empty(); } From 422b6da610361b43c3e1649f24b152747eda10f6 Mon Sep 17 00:00:00 2001 From: Muhammad Azeez Date: Thu, 5 Dec 2024 17:17:41 +0300 Subject: [PATCH 9/9] test http response --- samples/KitchenSink/Program.cs | 10 +++++++++- tests/Extism.Pdk.WasmTests/KitchenSinkTests.cs | 2 ++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/samples/KitchenSink/Program.cs b/samples/KitchenSink/Program.cs index e4c3810..bdcefdd 100644 --- a/samples/KitchenSink/Program.cs +++ b/samples/KitchenSink/Program.cs @@ -3,6 +3,7 @@ using Extism; using SampleLib; using System.Text.Json.Serialization; +using System.Collections.Generic; Class1.noop(); // Import Class1 from SampleLib so that it's included during compilation Console.WriteLine("Hello world!"); @@ -81,6 +82,12 @@ public static int GetTodo() request.Headers.Add("Authorization", $"Basic {token}"); var response = Pdk.SendRequest(request); + + if (!response.Headers.TryGetValue("content-type", out var contentType) || !contentType.Contains("application/json")) + { + Pdk.SetError($"Invalid content-type header. Expected 'application/json', got '{contentType}'"); + } + Pdk.SetOutput(response.Body); return 0; } @@ -88,13 +95,14 @@ public static int GetTodo() [UnmanagedCallersOnly(EntryPoint = "throw")] public static int Throw() { - // Exceptions are also handled, but Pdk.SetError is recommeded to use. + // Extism PDK also tries to handle Exceptions, but Pdk.SetError is recommeded to use. throw new InvalidOperationException("Something bad happened."); } } [JsonSerializable(typeof(ConcatInput))] [JsonSerializable(typeof(ConcatOutput))] + [JsonSerializable(typeof(Dictionary))] public partial class JsonContext : JsonSerializerContext {} public class ConcatInput diff --git a/tests/Extism.Pdk.WasmTests/KitchenSinkTests.cs b/tests/Extism.Pdk.WasmTests/KitchenSinkTests.cs index 1d7403a..ef03bb4 100644 --- a/tests/Extism.Pdk.WasmTests/KitchenSinkTests.cs +++ b/tests/Extism.Pdk.WasmTests/KitchenSinkTests.cs @@ -81,6 +81,8 @@ public void TestHttp(string token, string allowedHost, bool expected) manifest.AllowedHosts.Add(allowedHost); }); + plugin.AllowHttpResponseHeaders(); + var input = Encoding.UTF8.GetBytes("1"); if (expected)