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