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