|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Collections.Generic;
using System.Globalization;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using System.Diagnostics.CodeAnalysis;
using WasmAppBuilder;
//
// This class generates the icall_trampoline_dispatch () function used by the interpreter to call native code on WASM.
// It should be kept in sync with mono_wasm_interp_to_native_trampoline () in the runtime.
//
#nullable enable
internal sealed class InterpToNativeGenerator
{
private LogAdapter Log { get; set; }
public InterpToNativeGenerator(LogAdapter log) => Log = log;
public void Generate(IEnumerable<string> cookies, string outputPath)
{
using TempFileName tmpFileName = new();
using (var w = File.CreateText(tmpFileName.Path))
{
Emit(w, cookies);
}
if (Utils.CopyIfDifferent(tmpFileName.Path, outputPath, useHash: false))
Log.LogMessage(MessageImportance.Low, $"Generating managed2native table to '{outputPath}'.");
else
Log.LogMessage(MessageImportance.Low, $"Managed2native table in {outputPath} is unchanged.");
}
private static void Emit(StreamWriter w, IEnumerable<string> cookies)
{
w.WriteLine("""
/*
* GENERATED FILE, DON'T EDIT
* Generated by InterpToNativeGenerator
*/
#include "pinvoke.h"
#include <stdlib.h>
""");
// Use OrderBy because Order() is not available in net472
var signatures = cookies.OrderBy(c => c).Distinct().ToArray();
foreach (var signature in signatures)
{
try
{
w.WriteLine("static void");
w.WriteLine($"wasm_invoke_{signature.ToLower(CultureInfo.InvariantCulture)} (void *target_func, MonoInterpMethodArguments *margs)");
w.WriteLine("{");
w.Write($"\ttypedef {SignatureMapper.CharToNativeType(signature[0])} (*T)(");
for (int i = 1; i < signature.Length; ++i)
{
char p = signature[i];
if (i > 1)
w.Write(", ");
w.Write($"{SignatureMapper.CharToNativeType(p)} arg_{i - 1}");
}
if (signature.Length == 1)
w.Write("void");
w.WriteLine(");\n\tT func = (T)target_func;");
var ctx = new EmitCtx();
w.Write("\t");
if (!SignatureMapper.IsVoidSignature(signature))
w.Write($"{SignatureMapper.CharToNativeType(signature[0])} res = ");
w.Write("func (");
for (int i = 1; i < signature.Length; ++i)
{
char p = signature[i];
if (i > 1)
w.Write(", ");
w.Write(ctx.Emit(p));
}
w.WriteLine(");");
if (!SignatureMapper.IsVoidSignature(signature))
{
w.WriteLine($"\tvoid *retval = mono_wasm_interp_method_args_get_retval (margs);");
w.WriteLine($"\t*({SignatureMapper.CharToNativeType(signature[0])}*)retval = res;");
}
w.WriteLine("}\n");
}
catch (InvalidSignatureCharException e)
{
throw new LogAsErrorException($"Element '{e.Char}' of signature '{signature}' can't be handled by managed2native generator");
}
}
Array.Sort(signatures);
w.WriteLine("static void* interp_to_native_invokes[] = {");
foreach (var sig in signatures)
{
var lsig = sig.ToLower(CultureInfo.InvariantCulture);
w.WriteLine($"\twasm_invoke_{lsig},");
}
w.WriteLine("};");
w.WriteLine("static const char* interp_to_native_signatures[] = {");
foreach (var signature in signatures)
w.WriteLine($"\t\"{signature}\",");
w.WriteLine("};");
w.WriteLine($"static unsigned int interp_to_native_signatures_count = {signatures.Length};");
w.WriteLine();
w.WriteLine("""
static int
compare_icall_tramp (const void *key, const void *elem)
{
return strcmp (key, *(void**)elem);
}
static void*
mono_wasm_interp_to_native_callback (char* cookie)
{
void* p = bsearch (cookie, interp_to_native_signatures, interp_to_native_signatures_count, sizeof (void*), compare_icall_tramp);
if (!p)
return NULL;
int idx = (const char**)p - (const char**)interp_to_native_signatures;
return interp_to_native_invokes [idx];
};
""");
}
private sealed class EmitCtx
{
private int iarg, farg;
public string Emit(char c)
{
int argIndex;
switch (c)
{
case 'I':
argIndex = iarg;
iarg += 1;
break;
case 'L':
argIndex = iarg;
iarg += 2;
break;
case 'F':
case 'D':
argIndex = farg;
farg += 1;
break;
default:
throw new InvalidSignatureCharException(c);
}
return $"mono_wasm_interp_method_args_get_{char.ToLower(c, CultureInfo.InvariantCulture)}arg (margs, {argIndex})";
}
}
}
|