[tools] Add aot offsets generation tool.
[mono.git] / tools / offsets-tool / MonoAotOffsetsDumper.cs
1 using System;
2 using System.Collections.Generic;
3 using System.IO;
4 using System.Linq;
5 using System.Text.RegularExpressions;
6 using CppSharp.AST;
7 using CppSharp.AST.Extensions;
8 using CppSharp.Parser;
9
10 namespace CppSharp
11 {
12     /**
13      * This tool dumps the offsets of structures used in the Mono VM needed
14      * by the AOT compiler for cross-compiling code to target platforms
15      * different than the host the compiler is being invoked on.
16      * 
17      * It takes two arguments: the path to your clone of the Mono repo and
18      * the path to the root of Android NDK.
19      */
20     static class MonoAotOffsetsDumper
21     {
22         static string MonoDir = @"";
23
24         static List<string> Abis = new List<string> ();
25         static string OutputDir;
26
27         static string MonodroidDir = @"";
28         static string MaccoreDir = @"";
29
30         public enum TargetPlatform
31         {
32             Android,
33             iOS,
34             WatchOS,
35         }
36
37         public class Target
38         {
39             public Target()
40             {
41                 Defines = new List<string>();
42                 Arguments = new List<string>();
43             }
44
45             public Target(Target target)
46             {
47                 Platform = target.Platform;
48                 Triple = target.Triple;
49                 Build = target.Build;
50                 Defines = target.Defines;
51                 Arguments = target.Arguments;
52             }
53
54             public TargetPlatform Platform;
55             public string Triple;
56             public string Build;            
57             public List<string> Defines;
58             public List<string> Arguments;
59         };
60
61         public static List<Target> Targets = new List<Target>();
62
63         public static IEnumerable<Target> AndroidTargets
64         {
65             get { return Targets.Where ((t) => t.Platform == TargetPlatform.Android); }
66         }
67
68         public static IEnumerable<Target> DarwinTargets
69         {
70             get
71             {
72                 return Targets.Where ((t) => t.Platform == TargetPlatform.iOS ||
73                     t.Platform == TargetPlatform.WatchOS);
74             }
75         }
76
77         public static IEnumerable<Target> iOSTargets
78         {
79             get
80             {
81                 return Targets.Where ((t) => t.Platform == TargetPlatform.iOS);
82             }
83         }
84
85         public static void SetupAndroidTargets()
86         {
87             Targets.Add (new Target {
88                 Platform = TargetPlatform.Android,
89                 Triple = "i686-none-linux-android",
90                 Build = "mono-x86",
91                 Defines = { "TARGET_X86" }
92             });
93
94             Targets.Add (new Target {
95                 Platform = TargetPlatform.Android,
96                 Triple = "x86_64-none-linux-android",
97                 Build = "mono-x86_64",
98                 Defines = { "TARGET_AMD64" }
99             });            
100
101             Targets.Add (new Target {
102                 Platform = TargetPlatform.Android,
103                 Triple = "armv5-none-linux-androideabi",
104                 Build = "mono-armv6",
105                 Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5" }
106             });
107
108             Targets.Add (new Target {
109                 Platform = TargetPlatform.Android,
110                 Triple = "armv7-none-linux-androideabi",
111                 Build = "mono-armv7",                    
112                 Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5", "HAVE_ARMV6",
113                     "HAVE_ARMV7"
114                 }
115             });
116
117             Targets.Add (new Target {
118                 Platform = TargetPlatform.Android,
119                 Triple = "aarch64-v8a-linux-android",
120                 Build = "mono-aarch64",                    
121                 Defines = { "TARGET_ARM64" }
122             });            
123
124             /*Targets.Add(new Target {
125                     Platform = TargetPlatform.Android,
126                     Triple = "mipsel-none-linux-android",
127                     Build = "mono-mips",
128                     Defines = { "TARGET_MIPS", "__mips__" }
129                 });*/
130
131             foreach (var target in AndroidTargets)
132                 target.Defines.AddRange (new string[] { "PLATFORM_ANDROID",
133                     "TARGET_ANDROID", "MONO_CROSS_COMPILE", "USE_MONO_CTX"
134                 });
135         }
136
137         public static void SetupiOSTargets()
138         {
139             Targets.Add(new Target {
140                 Platform = TargetPlatform.iOS,
141                 Triple = "arm-apple-darwin10",
142                 Build = "target7",
143                 Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5" }
144             });
145
146             Targets.Add(new Target {
147                 Platform = TargetPlatform.iOS,
148                 Triple = "aarch64-apple-darwin10",
149                 Build = "target64",                    
150                 Defines = { "TARGET_ARM64" }
151             });
152
153             foreach (var target in iOSTargets) {
154                 target.Defines.AddRange (new string[] { "PLATFORM_DARWIN",
155                     "TARGET_IOS", "TARGET_MACH", "MONO_CROSS_COMPILE", "USE_MONO_CTX",
156                     "_XOPEN_SOURCE"
157                 });
158             }
159
160             Targets.Add(new Target {
161                 Platform = TargetPlatform.WatchOS,
162                 Triple = "armv7k-apple-darwin",
163                 Build = "targetwatch",
164                 Defines = { "TARGET_ARM", "ARM_FPU_VFP", "HAVE_ARMV5" }
165             });
166
167             foreach (var target in DarwinTargets) {
168                 target.Defines.AddRange (new string[] { "PLATFORM_DARWIN",
169                     "TARGET_IOS", "TARGET_MACH", "MONO_CROSS_COMPILE", "USE_MONO_CTX",
170                     "_XOPEN_SOURCE"
171                 });
172             }
173         }
174
175         static bool GetParentSubDirectoryPath(string parent, out string subdir)
176         {
177             var directory = Directory.GetParent(Directory.GetCurrentDirectory());
178
179             while (directory != null) {
180                 var path = Path.Combine(directory.FullName, parent);
181
182                 if (Directory.Exists (path)) {
183                     subdir = path;
184                     return true;
185                 }
186
187                 directory = directory.Parent;
188             }
189
190             subdir = null;
191             return false;
192         }
193
194         public static void Main(string[] args)
195         {
196             ParseCommandLineArgs(args);
197
198             string monodroidDir;
199             if (!Directory.Exists (MonodroidDir) &&
200                 GetParentSubDirectoryPath ("monodroid", out monodroidDir)) {
201                 MonodroidDir = Path.Combine (monodroidDir);
202             }
203
204             if (Directory.Exists (MonodroidDir))
205                 SetupAndroidTargets();
206
207             string maccoreDir;
208             if (!Directory.Exists (MaccoreDir) &&
209                 GetParentSubDirectoryPath ("maccore", out maccoreDir)) {
210                 MaccoreDir = Path.Combine (maccoreDir);
211             }
212
213             if (Directory.Exists(MaccoreDir))
214                 SetupiOSTargets();
215
216             foreach (var target in Targets)
217              {
218                 if (Abis.Any() && !Abis.Any (target.Triple.Contains))
219                     continue;
220                 
221                 Console.WriteLine();
222                 Console.WriteLine("Processing triple: {0}", target.Triple);
223
224                 var options = new DriverOptions();
225
226                 var log = new TextDiagnosticPrinter();
227                 var driver = new Driver(options, log);
228
229                 Setup(driver, target);
230                 driver.Setup();
231
232                 BuildParseOptions(driver, target);
233                 if (!driver.ParseCode())
234                     return;
235
236                 Dump(driver.ASTContext, driver.TargetInfo, target);
237             }
238         }
239
240         static void BuildParseOptions(Driver driver, Target target)
241         {
242             foreach (var header in driver.Options.Headers)
243             {
244                 var source = driver.Project.AddFile(header);
245                 source.Options = driver.BuildParseOptions(source);
246
247                 if (header.Contains ("mini"))
248                     continue;
249
250                 source.Options.addDefines ("HAVE_SGEN_GC");
251                 source.Options.addDefines ("HAVE_MOVING_COLLECTOR");
252             }
253         }
254
255         static string GetAndroidNdkPath()
256         {
257             // Find the Android NDK's path from Monodroid's config.
258             var configFile = Path.Combine(MonodroidDir, "env.config");
259             if (!File.Exists(configFile))
260                 throw new Exception("Expected a valid Monodroid environment config file at " + configFile);
261
262             var config = File.ReadAllText(configFile);
263             var match = Regex.Match(config, @"ANDROID_NDK_PATH\s*:=\s(.*)");
264             return match.Groups[1].Value.Trim();
265         }
266
267         static void ParseCommandLineArgs(string[] args)
268         {
269             var showHelp = false;
270
271             var options = new Mono.Options.OptionSet () {
272                 { "abi=", "ABI triple to generate", v => Abis.Add(v) },
273                 { "o|out=", "output directory", v => OutputDir = v },
274                 { "maccore=", "include directory", v => MaccoreDir = v },
275                 { "monodroid=", "include directory", v => MonodroidDir = v },
276                 { "mono=", "include directory", v => MonoDir = v },
277                 { "h|help",  "show this message and exit",  v => showHelp = v != null },
278             };
279
280             try {
281                 options.Parse (args);
282             }
283             catch (Mono.Options.OptionException e) {
284                 Console.WriteLine (e.Message);
285                 Environment.Exit(0);
286             }
287
288             if (showHelp)
289             {
290                 // Print usage and exit.
291                 Console.WriteLine("{0} [--abi=triple] [--out=dir] "
292                     + "[--monodroid/maccore=dir] [--mono=dir]",
293                     AppDomain.CurrentDomain.FriendlyName);
294                 Environment.Exit(0);
295             }
296         }
297
298         static void Setup(Driver driver, Target target)
299         {
300             var options = driver.Options;
301             options.DryRun = true;
302             options.Verbose = false;
303             options.LibraryName = "Mono";
304             options.MicrosoftMode = false;
305             options.addArguments("-xc");
306             options.addArguments("-std=gnu99");
307             options.addDefines("CPPSHARP");
308
309             foreach (var define in target.Defines)
310                 options.addDefines(define);
311
312             SetupToolchainPaths(driver, target);
313
314             SetupMono(options, target);
315         }
316
317         static void SetupMono(DriverOptions options, Target target)
318         {
319             string targetPath;
320             switch (target.Platform) {
321             case TargetPlatform.Android:
322                 targetPath = Path.Combine (MonodroidDir, "builds");
323                 break;
324             case TargetPlatform.WatchOS:
325             case TargetPlatform.iOS:
326                 targetPath = Path.Combine (MaccoreDir, "builds");
327                 break;
328             default:
329                 throw new ArgumentOutOfRangeException ();
330             }
331
332             if (!Directory.Exists (MonoDir)) {
333                 MonoDir = Path.GetFullPath (Path.Combine (targetPath, "../../mono"));
334             }
335
336             var targetBuild = Path.Combine(targetPath, target.Build);
337
338             if (!Directory.Exists(targetBuild))
339                 throw new Exception(string.Format("Could not find the target build directory: {0}", targetBuild));
340
341             var includeDirs = new[]
342             {
343                 targetBuild,
344                 Path.Combine(targetBuild, "eglib", "src"),
345                 MonoDir,
346                 Path.Combine(MonoDir, "mono"),
347                 Path.Combine(MonoDir, "mono", "mini"),
348                 Path.Combine(MonoDir, "eglib", "src")
349             };
350
351             foreach (var inc in includeDirs)
352                 options.addIncludeDirs(inc);
353
354             var filesToParse = new[]
355             {
356                 Path.Combine(MonoDir, "mono", "metadata", "metadata-cross-helpers.c"),
357                 Path.Combine(MonoDir, "mono", "mini", "mini-cross-helpers.c"),
358             };
359
360             foreach (var file in filesToParse)
361                 options.Headers.Add(file);
362         }
363
364         static void SetupMSVC(Driver driver, string triple)
365         {
366             var options = driver.Options;
367
368             options.Abi = Parser.AST.CppAbi.Microsoft;
369             options.MicrosoftMode = true;
370
371             var systemIncludeDirs = new[]
372             {
373                 @"C:\Program Files (x86)\Windows Kits\8.1\Include\um",
374                 @"C:\Program Files (x86)\Windows Kits\8.1\Include\shared"
375             };
376
377             foreach (var inc in systemIncludeDirs)
378                 options.addSystemIncludeDirs(inc);
379
380             options.addDefines("HOST_WIN32");
381         }
382
383         static void SetupToolchainPaths(Driver driver, Target target)
384         {
385             switch (target.Platform) {
386             case TargetPlatform.Android:
387                 SetupAndroidNDK(driver, target);
388                 break;
389             case TargetPlatform.iOS:
390             case TargetPlatform.WatchOS:
391                 SetupXcode(driver, target);
392                 break;
393             default:
394                 throw new ArgumentOutOfRangeException ();
395             }
396         }        
397
398         static string GetArchFromTriple(string triple)
399         {
400             if (triple.Contains("mips"))
401                 return "mips";
402
403             if (triple.Contains("arm64") || triple.Contains("aarch64"))
404                 return "arm64";
405
406             if (triple.Contains("arm"))
407                 return "arm";
408
409             if (triple.Contains("i686"))
410                 return "x86";
411
412             if (triple.Contains("x86_64"))
413                 return "x86_64";
414
415             throw  new Exception("Unknown architecture from triple: " + triple);
416         }
417
418         static string GetXcodeToolchainPath()
419         {
420             var toolchains = Directory.EnumerateDirectories("/Applications", "Xcode*")
421                 .ToList();
422             toolchains.Sort();
423
424             var toolchainPath = toolchains.LastOrDefault();
425             if (toolchainPath == null)
426                 throw new Exception("Could not find a valid Xcode SDK");
427
428             return toolchainPath;
429         }
430
431         static string GetXcodeBuiltinIncludesFolder()
432         {
433             var toolchainPath = GetXcodeToolchainPath();
434
435             var toolchains = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
436                 "Contents/Developer/Toolchains")).ToList();
437             toolchains.Sort();
438
439             toolchainPath = toolchains.LastOrDefault();
440             if (toolchainPath == null)
441                 throw new Exception("Could not find a valid Xcode toolchain");
442
443             var includePaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
444                 "usr/lib/clang")).ToList();
445             var includePath = includePaths.LastOrDefault();
446
447             if (includePath == null)
448                 throw new Exception("Could not find a valid Clang include folder");
449
450             return Path.Combine(includePath, "include");
451         }
452
453         static string GetXcodeiOSIncludesFolder()
454         {
455             var toolchainPath = GetXcodeToolchainPath();
456
457             var sdkPaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
458                 "Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs")).ToList();
459             var sdkPath = sdkPaths.LastOrDefault();
460
461             if (sdkPath == null)
462                 throw new Exception("Could not find a valid iPhone SDK");
463
464             return Path.Combine(sdkPath, "usr/include");
465         }
466
467         static string GetXcodeWatchOSIncludesFolder()
468         {
469             var toolchainPath = GetXcodeToolchainPath();
470
471             var sdkPaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
472                 "Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs")).ToList();
473             var sdkPath = sdkPaths.LastOrDefault();
474
475             if (sdkPath == null)
476                 throw new Exception("Could not find a valid WatchOS SDK");
477
478             return Path.Combine(sdkPath, "usr/include");
479         }
480
481         static void SetupXcode(Driver driver, Target target)
482         {
483             var options = driver.Options;
484
485             var builtinsPath = GetXcodeBuiltinIncludesFolder();
486             string includePath;
487
488             switch (target.Platform) {
489             case TargetPlatform.iOS:
490                 includePath = GetXcodeiOSIncludesFolder();
491                 break;
492             case TargetPlatform.WatchOS:
493                 includePath = GetXcodeWatchOSIncludesFolder();
494                 break;
495             default:
496                 throw new ArgumentOutOfRangeException ();
497             }
498
499             options.addSystemIncludeDirs(builtinsPath);
500             options.addSystemIncludeDirs(includePath);
501
502             options.NoBuiltinIncludes = true;
503             options.NoStandardIncludes = true;
504             options.TargetTriple = target.Triple;
505         }
506
507         static string GetAndroidHostToolchainPath()
508         {
509             var androidNdkPath = GetAndroidNdkPath ();
510             var toolchains = Directory.EnumerateDirectories(
511                 Path.Combine(androidNdkPath, "toolchains"), "llvm*").ToList();
512             toolchains.Sort();
513
514             var toolchainPath = toolchains.LastOrDefault();
515             if (toolchainPath == null)
516                 throw new Exception("Could not find a valid NDK host toolchain");
517
518             toolchains = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
519                 "prebuilt")).ToList();
520             toolchains.Sort();
521
522             toolchainPath = toolchains.LastOrDefault();
523             if (toolchainPath == null)
524                 throw new Exception("Could not find a valid NDK host toolchain");
525
526             return toolchainPath;
527         }
528
529         static string GetAndroidBuiltinIncludesFolder()
530         {
531             var toolchainPath = GetAndroidHostToolchainPath();
532
533             string clangToolchainPath = Path.Combine(toolchainPath, "lib64", "clang");
534             if (!Directory.Exists (clangToolchainPath))
535                 clangToolchainPath = Path.Combine(toolchainPath, "lib", "clang");
536
537             string includePath = null;
538             if (Directory.Exists (clangToolchainPath)) {
539                 var includePaths = Directory.EnumerateDirectories(clangToolchainPath).ToList();
540                 includePath = includePaths.LastOrDefault();
541             }
542             if (includePath == null)
543                 throw new Exception("Could not find a valid Clang include folder");
544
545             return Path.Combine(includePath, "include");
546         }
547
548         static void SetupAndroidNDK(Driver driver, Target target)
549         {
550             var options = driver.Options;
551
552             var builtinsPath = GetAndroidBuiltinIncludesFolder();
553             options.addSystemIncludeDirs(builtinsPath);
554
555             var androidNdkRoot = GetAndroidNdkPath ();
556             const int androidNdkApiLevel = 21;
557
558             var toolchainPath = Path.Combine(androidNdkRoot, "platforms",
559                 "android-" + androidNdkApiLevel, "arch-" + GetArchFromTriple(target.Triple),
560                 "usr", "include");
561             options.addSystemIncludeDirs(toolchainPath);
562
563             options.NoBuiltinIncludes = true;
564             options.NoStandardIncludes = true;
565             options.TargetTriple = target.Triple;
566         }
567
568         static uint GetTypeAlign(ParserTargetInfo target, ParserIntType type)
569         {
570             switch (type)
571             {
572                 case ParserIntType.SignedChar:
573                 case ParserIntType.UnsignedChar:
574                     return target.CharAlign;
575                 case ParserIntType.SignedShort:
576                 case ParserIntType.UnsignedShort:
577                     return target.ShortAlign;
578                 case ParserIntType.SignedInt:
579                 case ParserIntType.UnsignedInt:
580                     return target.IntAlign;
581                 case ParserIntType.SignedLong:
582                 case ParserIntType.UnsignedLong:
583                     return target.LongAlign;
584                 case ParserIntType.SignedLongLong:
585                 case ParserIntType.UnsignedLongLong:
586                     return target.LongLongAlign;
587                 default:
588                     throw new Exception("Type has no alignment");
589             }
590         }
591
592         static uint GetTypeSize(ParserTargetInfo target, ParserIntType type)
593         {
594             switch (type)
595             {
596                 case ParserIntType.SignedChar:
597                 case ParserIntType.UnsignedChar:
598                     return target.CharWidth;
599                 case ParserIntType.SignedShort:
600                 case ParserIntType.UnsignedShort:
601                     return target.ShortWidth;
602                 case ParserIntType.SignedInt:
603                 case ParserIntType.UnsignedInt:
604                     return target.IntWidth;
605                 case ParserIntType.SignedLong:
606                 case ParserIntType.UnsignedLong:
607                     return target.LongWidth;
608                 case ParserIntType.SignedLongLong:
609                 case ParserIntType.UnsignedLongLong:
610                     return target.LongLongWidth;
611                 default:
612                     throw new Exception("Type has no size");
613             }
614         }
615
616         static string GetTargetPlatformDefine(TargetPlatform target)
617         {
618             switch (target) {
619             case TargetPlatform.Android:
620                 return "TARGET_ANDROID";
621             case TargetPlatform.iOS:
622                 return "TARGET_IOS";
623             case TargetPlatform.WatchOS:
624                 return "TARGET_WATCHOS";
625             default:
626                 throw new ArgumentOutOfRangeException ();
627             }
628         }
629
630         static void Dump(ASTContext ctx, ParserTargetInfo targetInfo, Target target)
631         {
632             var targetFile = target.Triple;
633
634                         if (!string.IsNullOrEmpty (OutputDir))
635                                 targetFile = Path.Combine (OutputDir, targetFile);
636
637             targetFile += ".h";
638
639             using (var writer = new StreamWriter(targetFile))
640             //using (var writer = Console.Out)
641             {
642                 writer.WriteLine("#ifndef USED_CROSS_COMPILER_OFFSETS");
643                 writer.WriteLine("#ifdef {0}", target.Defines[0]);
644                 writer.WriteLine ("#ifdef {0}", GetTargetPlatformDefine (target.Platform));
645                 writer.WriteLine("#ifndef HAVE_BOEHM_GC");
646                 writer.WriteLine("#define HAS_CROSS_COMPILER_OFFSETS");
647                 writer.WriteLine("#if defined (USE_CROSS_COMPILE_OFFSETS) || defined (MONO_CROSS_COMPILE)");
648                 writer.WriteLine("#if !defined (DISABLE_METADATA_OFFSETS)");
649                 writer.WriteLine("#define USED_CROSS_COMPILER_OFFSETS");
650
651                 DumpAligns(writer, targetInfo);
652                 DumpSizes(writer, targetInfo);
653                 DumpMetadataOffsets(writer, ctx, target);
654
655                 writer.WriteLine("#endif //disable metadata check");
656
657                 DumpJITOffsets(writer, ctx);
658
659                 writer.WriteLine("#endif //cross compiler checks");
660                 writer.WriteLine("#endif //gc check");
661                 writer.WriteLine("#endif //os check");
662                 writer.WriteLine("#endif //arch check");
663                 writer.WriteLine("#endif //USED_CROSS_COMPILER_OFFSETS check");
664             }
665
666             Console.WriteLine("Generated offsets file: {0}", targetFile);
667         }
668
669         static void DumpAligns(TextWriter writer, ParserTargetInfo target)
670         {
671             var aligns = new[]
672             {
673                 new { Name = "gint8", Align = target.CharAlign},
674                 new { Name = "gint16", Align = target.ShortAlign},
675                 new { Name = "gint32", Align = target.IntAlign},
676                 new { Name = "gint64", Align = GetTypeAlign(target, target.Int64Type)},
677                 new { Name = "float", Align = target.FloatAlign},
678                 new { Name = "double", Align = target.DoubleAlign},
679                 new { Name = "gpointer", Align = GetTypeAlign(target, target.IntPtrType)},
680             };
681
682             // Write the alignment info for the basic types.
683             foreach (var align in aligns)
684                 writer.WriteLine("DECL_ALIGN2({0},{1})", align.Name, align.Align / 8);
685         }
686
687         static void DumpSizes(TextWriter writer, ParserTargetInfo target)
688         {
689             var sizes = new[]
690             {
691                 new { Name = "gint8", Size = target.CharWidth},
692                 new { Name = "gint16", Size = target.ShortWidth},
693                 new { Name = "gint32", Size = target.IntWidth},
694                 new { Name = "gint64", Size = GetTypeSize(target, target.Int64Type)},
695                 new { Name = "float", Size = target.FloatWidth},
696                 new { Name = "double", Size = target.DoubleWidth},
697                 new { Name = "gpointer", Size = GetTypeSize(target, target.IntPtrType)},
698             };
699
700             // Write the size info for the basic types.
701             foreach (var size in sizes)
702                 writer.WriteLine("DECL_SIZE2({0},{1})", size.Name, size.Size / 8);
703         }
704
705         static Class GetClassFromTypedef(ITypedDecl typedef)
706         {
707             var type = typedef.Type.Desugar() as TagType;
708             if (type == null)
709                 return null;
710
711             var @class = type.Declaration as Class;
712
713             return @class.IsIncomplete ?
714                 (@class.CompleteDeclaration as Class) : @class; 
715         }
716
717         static void DumpClasses(TextWriter writer, ASTContext ctx, IEnumerable<string> types,
718             bool optional = false)
719         {
720             foreach (var @struct in types)
721             {
722                 var @class = ctx.FindCompleteClass(@struct);
723                 if (@class == null)
724                     @class = ctx.FindCompleteClass("_" + @struct);
725
726                 if (@class == null)
727                 {
728                     var typedef = ctx.FindTypedef(@struct).FirstOrDefault(
729                         decl => !decl.IsIncomplete);
730
731                     if (typedef != null)
732                         @class = GetClassFromTypedef(typedef);
733                 }
734
735                 if (@class == null && optional)
736                     continue;
737
738                 if (@class == null)
739                     throw new Exception("Expected to find struct definition for " + @struct);
740
741                 DumpStruct(writer, @class);
742             }
743         }
744
745         static void DumpMetadataOffsets(TextWriter writer, ASTContext ctx, Target target)
746         {
747             var types = new List<string>
748             {
749                 "MonoObject",
750                 "MonoClass",
751                 "MonoVTable",
752                 "MonoDelegate",
753                 "MonoInternalThread",
754                 "MonoMulticastDelegate",
755                 "MonoTransparentProxy",
756                 "MonoRealProxy",
757                 "MonoRemoteClass",
758                 "MonoArray",
759                 "MonoArrayBounds",
760                 "MonoSafeHandle",
761                 "MonoHandleRef",
762                 "MonoComInteropProxy",
763                 "MonoString",
764                 "MonoException",
765                 "MonoTypedRef",
766                 "MonoThreadsSync",
767                 "SgenThreadInfo"
768             };
769
770             DumpClasses(writer, ctx, types);
771         }
772
773         static void DumpJITOffsets(TextWriter writer, ASTContext ctx)
774         {
775             writer.WriteLine("#ifndef DISABLE_JIT_OFFSETS");
776             writer.WriteLine("#define USED_CROSS_COMPILER_OFFSETS");
777
778             var types = new[]
779             {
780                 "MonoLMF",
781                 "MonoMethodRuntimeGenericContext",
782                 "MonoJitTlsData",
783                 "MonoGSharedVtMethodRuntimeInfo",
784                 "MonoContinuation",
785                 "MonoContext",
786                 "MonoDelegateTrampInfo",
787             };
788
789             DumpClasses(writer, ctx, types);
790
791             var optionalTypes = new[]
792             {
793                 "GSharedVtCallInfo",
794                 "SeqPointInfo",
795                 "DynCallArgs", 
796                 "MonoLMFTramp",
797             };            
798
799             DumpClasses(writer, ctx, optionalTypes, optional: true);
800
801             writer.WriteLine("#endif //disable jit check");
802         }
803
804         static void DumpStruct(TextWriter writer, Class @class)
805         {
806             var name = @class.Name;
807                         if (name.StartsWith ("_", StringComparison.Ordinal))
808                                 name = name.Substring (1);
809
810             foreach (var field in @class.Fields)
811             {
812                 if (field.IsBitField) continue;
813
814                 if (name == "SgenThreadInfo" && field.Name == "regs")
815                     continue;
816
817                 writer.WriteLine("DECL_OFFSET2({0},{1},{2})", name, field.Name,
818                     field.Offset / 8);
819             }
820         }
821     }
822 }