2 // tuner.cs: WebAssembly build time helpers
10 using System
.Collections
.Generic
;
13 class Icall
: IComparable
<Icall
>
15 public Icall (string name
, string func
, bool handles
) {
23 public string Assembly
;
25 public int TokenIndex
;
27 public int CompareTo (Icall other
) {
28 return TokenIndex
- other
.TokenIndex
;
33 public IcallClass (string name
) {
35 Icalls
= new Dictionary
<string, Icall
> ();
39 public Dictionary
<string, Icall
> Icalls
;
44 public Pinvoke (string entry_point
, string module
, MethodReference method
) {
45 EntryPoint
= entry_point
;
50 public string EntryPoint
;
52 public MethodReference Method
;
55 public class WasmTuner
57 public static int Main (String
[] args
) {
58 return new WasmTuner ().Run (args
);
63 Dictionary
<string, IcallClass
> runtime_icalls
;
65 List
<Pinvoke
> pinvokes
;
67 // Read the icall table generated by mono --print-icall-table
68 void ReadTable (string filename
) {
70 using (var stream
= File
.OpenText (filename
)) {
71 json
= JsonValue
.Load (stream
);
74 runtime_icalls
= new Dictionary
<string, IcallClass
> ();
75 var arr
= (JsonArray
)json
;
76 foreach (var v
in arr
) {
77 if ((string)v
["klass"] == "")
80 var icall_class
= new IcallClass ((string)v
["klass"]);
81 runtime_icalls
[icall_class
.Name
] = icall_class
;
82 foreach (JsonObject icall_j
in v
["icalls"]) {
83 if (icall_j
.Count
== 0)
85 string name
= (string)icall_j
["name"];
86 string func
= (string)icall_j
["func"];
87 bool handles
= (bool)icall_j
["handles"];
89 icall_class
.Icalls
[name
] = new Icall (name
, func
, handles
);
95 Console
.WriteLine ("Usage: tuner.exe <arguments>");
96 Console
.WriteLine ("Arguments:");
97 Console
.WriteLine ("--gen-icall-table icall-table.json <assemblies>.");
98 Console
.WriteLine ("--gen-pinvoke-table <list of native library names separated by commas> <assemblies>.");
101 int Run (String
[] args
) {
102 if (args
.Length
< 1) {
106 string cmd
= args
[0];
107 if (cmd
== "--gen-icall-table") {
108 if (args
.Length
< 3) {
112 return GenIcallTable (args
);
113 } else if (cmd
== "--gen-pinvoke-table") {
114 return GenPinvokeTable (args
);
121 static string MapType (TypeReference t
) {
122 if (t
.Name
== "Void")
124 else if (t
.Name
== "Double")
126 else if (t
.Name
== "Float")
132 static string GenPinvokeDecl (Pinvoke pinvoke
) {
133 var sb
= new StringBuilder ();
134 var method
= pinvoke
.Method
;
135 sb
.Append (MapType (method
.ReturnType
));
136 sb
.Append ($" {pinvoke.EntryPoint} (");
138 foreach (var p
in method
.Parameters
) {
141 sb
.Append (MapType (method
.Parameters
[pindex
].ParameterType
));
145 return sb
.ToString ();
148 int GenPinvokeTable (String
[] args
) {
149 var modules
= new Dictionary
<string, string> ();
150 foreach (var module
in args
[1].Split (','))
151 modules
[module
] = module
;
153 args
= args
.Skip (2).ToArray ();
154 pinvokes
= new List
<Pinvoke
> ();
155 foreach (var fname
in args
) {
156 var a
= AssemblyDefinition
.ReadAssembly (fname
);
158 foreach (var type
in a
.MainModule
.Types
) {
159 ProcessTypeForPinvoke (type
);
160 foreach (var nested
in type
.NestedTypes
)
161 ProcessTypeForPinvoke (nested
);
165 Console
.WriteLine ("// GENERATED FILE, DO NOT MODIFY");
166 Console
.WriteLine ("typedef struct {");
167 Console
.WriteLine ("const char *name;");
168 Console
.WriteLine ("void *func;");
169 Console
.WriteLine ("} PinvokeImport;");
170 Console
.WriteLine ();
172 foreach (var pinvoke
in pinvokes
) {
173 if (modules
.ContainsKey (pinvoke
.Module
))
174 Console
.WriteLine (GenPinvokeDecl (pinvoke
));
177 foreach (var module
in modules
.Keys
) {
178 string symbol
= module
.Replace (".", "_") + "_imports";
179 Console
.WriteLine ("static PinvokeImport " + symbol
+ " [] = {");
180 foreach (var pinvoke
in pinvokes
) {
181 if (pinvoke
.Module
== module
)
182 Console
.WriteLine ("{\"" + pinvoke.EntryPoint + "\", " + pinvoke.EntryPoint + "},");
184 Console
.WriteLine ("{NULL, NULL}");
185 Console
.WriteLine ("};");
187 Console
.Write ("static void *pinvoke_tables[] = { ");
188 foreach (var module
in modules
.Keys
) {
189 string symbol
= module
.Replace (".", "_") + "_imports";
190 Console
.Write (symbol
+ ",");
192 Console
.WriteLine ("};");
193 Console
.Write ("static char *pinvoke_names[] = { ");
194 foreach (var module
in modules
.Keys
) {
195 Console
.Write ("\"" + module
+ "\"" + ",");
197 Console
.WriteLine ("};");
202 void ProcessTypeForPinvoke (TypeDefinition type
) {
203 foreach (var method
in type
.Methods
) {
204 var info
= method
.PInvokeInfo
;
207 pinvokes
.Add (new Pinvoke (info
.EntryPoint
, info
.Module
.Name
, method
));
212 // Given the runtime generated icall table, and a set of assemblies, generate
213 // a smaller linked icall table mapping tokens to C function names
215 int GenIcallTable (String
[] args
) {
216 var icall_table_filename
= args
[1];
217 args
= args
.Skip (2).ToArray ();
219 ReadTable (icall_table_filename
);
221 icalls
= new List
<Icall
> ();
223 foreach (var fname
in args
) {
224 var a
= AssemblyDefinition
.ReadAssembly (fname
);
226 foreach (var type
in a
.MainModule
.Types
) {
228 foreach (var nested
in type
.NestedTypes
)
229 ProcessType (nested
);
233 var assemblies
= new Dictionary
<string, string> ();
234 foreach (var icall
in icalls
)
235 assemblies
[icall
.Assembly
] = icall
.Assembly
;
237 foreach (var assembly
in assemblies
.Keys
) {
238 var sorted
= icalls
.Where (i
=> i
.Assembly
== assembly
).ToArray ();
241 Console
.WriteLine ($"#define ICALL_TABLE_{assembly} 1\n");
243 Console
.WriteLine ($"static int {assembly}_icall_indexes [] = {{");
244 foreach (var icall
in sorted
)
245 Console
.WriteLine (String
.Format ("{0},", icall
.TokenIndex
));
246 Console
.WriteLine ("};");
247 foreach (var icall
in sorted
)
248 Console
.WriteLine (String
.Format ("void {0} ();", icall
.Func
));
249 Console
.WriteLine ($"static void *{assembly}_icall_funcs [] = {{");
250 foreach (var icall
in sorted
) {
251 Console
.WriteLine (String
.Format ("// token {0},", icall
.TokenIndex
));
252 Console
.WriteLine (String
.Format ("{0},", icall
.Func
));
254 Console
.WriteLine ("};");
255 Console
.WriteLine ($"static uint8_t {assembly}_icall_handles [] = {{");
256 foreach (var icall
in sorted
)
257 Console
.WriteLine (String
.Format ("{0},", icall
.Handles
? "1" : "0"));
258 Console
.WriteLine ("};");
264 // Append the type name used by the runtime icall tables
265 void AppendType (StringBuilder sb
, TypeReference t
) {
266 switch (t
.MetadataType
) {
267 case MetadataType
.Char
:
270 case MetadataType
.Boolean
:
273 case MetadataType
.Byte
:
276 case MetadataType
.SByte
:
279 case MetadataType
.Int16
:
282 case MetadataType
.UInt16
:
283 sb
.Append ("uint16");
285 case MetadataType
.Int32
:
288 case MetadataType
.UInt32
:
291 case MetadataType
.Int64
:
294 case MetadataType
.UInt64
:
297 case MetadataType
.IntPtr
:
298 sb
.Append ("intptr");
300 case MetadataType
.UIntPtr
:
301 sb
.Append ("uintptr");
303 case MetadataType
.Single
:
304 sb
.Append ("single");
306 case MetadataType
.Double
:
307 sb
.Append ("double");
309 case MetadataType
.Object
:
310 sb
.Append ("object");
312 case MetadataType
.String
:
313 sb
.Append ("string");
315 case MetadataType
.Array
:
316 AppendType (sb
, (t
as TypeSpecification
).ElementType
);
319 case MetadataType
.ByReference
:
320 AppendType (sb
, (t
as TypeSpecification
).ElementType
);
323 case MetadataType
.Pointer
:
324 AppendType (sb
, (t
as TypeSpecification
).ElementType
);
328 sb
.Append (t
.FullName
);
333 void ProcessType (TypeDefinition type
) {
334 foreach (var method
in type
.Methods
) {
335 if ((method
.ImplAttributes
& MethodImplAttributes
.InternalCall
) == 0)
338 if (method
.Name
== ".ctor")
341 var klass_name
= method
.DeclaringType
.FullName
;
342 if (!runtime_icalls
.ContainsKey (klass_name
))
343 // Registered at runtime
346 var klass
= runtime_icalls
[method
.DeclaringType
.FullName
];
351 if (klass
.Icalls
.ContainsKey (method
.Name
)) {
352 icall
= klass
.Icalls
[method
.Name
];
355 // Then with signature
356 var sig
= new StringBuilder (method
.Name
+ "(");
358 foreach (var par
in method
.Parameters
) {
361 var t
= par
.ParameterType
;
367 if (klass
.Icalls
.ContainsKey (sig
.ToString ())) {
368 icall
= klass
.Icalls
[sig
.ToString ()];
372 // Registered at runtime
375 icall
.TokenIndex
= (int)method
.MetadataToken
.RID
;
376 icall
.Assembly
= method
.DeclaringType
.Module
.Assembly
.Name
.Name
;