[interp] add InterpMethodArguments struct to offset-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 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 driver = new Driver(options);
223
224                 Setup(driver, target);
225                 driver.Setup();
226
227                 BuildParseOptions(driver, target);
228                 if (!driver.ParseCode())
229                     return;
230
231                 Dump(driver.Context.ASTContext, driver.Context.TargetInfo, target);
232             }
233         }
234
235         static void BuildParseOptions(Driver driver, Target target)
236         {
237             foreach (var header in driver.Options.Headers)
238             {
239                 var source = driver.Project.AddFile(header);
240                 source.Options = driver.BuildParserOptions(source);
241
242                 if (header.Contains ("mini"))
243                     continue;
244
245                 source.Options.AddDefines ("HAVE_SGEN_GC");
246                 source.Options.AddDefines ("HAVE_MOVING_COLLECTOR");
247             }
248         }
249
250         static string GetAndroidNdkPath()
251         {
252             if (!String.IsNullOrEmpty (AndroidNdkPath))
253                 return AndroidNdkPath;
254
255             // Find the Android NDK's path from Monodroid's config.
256             var configFile = Path.Combine(MonodroidDir, "env.config");
257             if (!File.Exists(configFile))
258                 throw new Exception("Expected a valid Monodroid environment config file at " + configFile);
259
260             var config = File.ReadAllText(configFile);
261             var match = Regex.Match(config, @"ANDROID_NDK_PATH\s*:=\s(.*)");
262             return match.Groups[1].Value.Trim();
263         }
264
265         static void ParseCommandLineArgs(string[] args)
266         {
267             var showHelp = false;
268
269             var options = new Mono.Options.OptionSet () {
270                 { "abi=", "ABI triple to generate", v => Abis.Add(v) },
271                 { "o|out=", "output directory", v => OutputDir = v },
272                 { "maccore=", "include directory", v => MaccoreDir = v },
273                 { "monodroid=", "top monodroid directory", v => MonodroidDir = v },
274                 { "android-ndk=", "Path to Android NDK", v => AndroidNdkPath = v },
275                 { "targetdir=", "Path to the directory containing the mono build", v =>TargetDir = 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} <options>",
292                     AppDomain.CurrentDomain.FriendlyName);
293                 options.WriteOptionDescriptions (Console.Out);
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.LibraryName = "Mono";
303
304             var parserOptions = driver.ParserOptions;
305             parserOptions.Verbose = false;
306             parserOptions.MicrosoftMode = false;
307             parserOptions.AddArguments("-xc");
308             parserOptions.AddArguments("-std=gnu99");
309             parserOptions.AddDefines("CPPSHARP");
310
311             foreach (var define in target.Defines)
312                 parserOptions.AddDefines(define);
313
314             SetupToolchainPaths(driver, target);
315
316             SetupMono(driver, target);
317         }
318
319         static void SetupMono(Driver driver, Target target)
320         {
321             string targetBuild;
322             switch (target.Platform) {
323             case TargetPlatform.Android:
324                 if (TargetDir == "") {
325                     Console.Error.WriteLine ("The --targetdir= option is required when targeting android.");
326                     Environment.Exit (1);
327                 }
328                 if (MonoDir == "") {
329                     Console.Error.WriteLine ("The --mono= option is required when targeting android.");
330                     Environment.Exit (1);
331                 }
332                 if (Abis.Count != 1) {
333                     Console.Error.WriteLine ("Exactly one --abi= argument is required when targeting android.");
334                     Environment.Exit (1);
335                 }
336                 targetBuild = TargetDir;
337                 break;
338             case TargetPlatform.WatchOS:
339             case TargetPlatform.iOS: {
340                 string targetPath = Path.Combine (MaccoreDir, "builds");
341                 if (!Directory.Exists (MonoDir))
342                     MonoDir = Path.GetFullPath (Path.Combine (targetPath, "../../mono"));
343                 targetBuild = Path.Combine(targetPath, target.Build);
344                 break;
345             }
346             default:
347                 throw new ArgumentOutOfRangeException ();
348             }
349
350             if (!Directory.Exists(targetBuild))
351                 throw new Exception(string.Format("Could not find the target build directory: {0}", targetBuild));
352
353             var includeDirs = new[]
354             {
355                 targetBuild,
356                 Path.Combine(targetBuild, "eglib", "src"),
357                 MonoDir,
358                 Path.Combine(MonoDir, "mono"),
359                 Path.Combine(MonoDir, "mono", "mini"),
360                 Path.Combine(MonoDir, "eglib", "src")
361             };
362
363             foreach (var inc in includeDirs)
364                 driver.ParserOptions.AddIncludeDirs(inc);
365
366             var filesToParse = new[]
367             {
368                 Path.Combine(MonoDir, "mono", "metadata", "metadata-cross-helpers.c"),
369                 Path.Combine(MonoDir, "mono", "mini", "mini-cross-helpers.c"),
370             };
371
372             foreach (var file in filesToParse)
373                 driver.Options.Headers.Add(file);
374         }
375
376         static void SetupMSVC(Driver driver, string triple)
377         {
378             var parserOptions = driver.ParserOptions;
379
380             parserOptions.Abi = Parser.AST.CppAbi.Microsoft;
381             parserOptions.MicrosoftMode = true;
382
383             var systemIncludeDirs = new[]
384             {
385                 @"C:\Program Files (x86)\Windows Kits\8.1\Include\um",
386                 @"C:\Program Files (x86)\Windows Kits\8.1\Include\shared"
387             };
388
389             foreach (var inc in systemIncludeDirs)
390                 parserOptions.AddSystemIncludeDirs(inc);
391
392             parserOptions.AddDefines("HOST_WIN32");
393         }
394
395         static void SetupToolchainPaths(Driver driver, Target target)
396         {
397             switch (target.Platform) {
398             case TargetPlatform.Android:
399                 SetupAndroidNDK(driver, target);
400                 break;
401             case TargetPlatform.iOS:
402             case TargetPlatform.WatchOS:
403                 SetupXcode(driver, target);
404                 break;
405             default:
406                 throw new ArgumentOutOfRangeException ();
407             }
408         }        
409
410         static string GetArchFromTriple(string triple)
411         {
412             if (triple.Contains("mips"))
413                 return "mips";
414
415             if (triple.Contains("arm64") || triple.Contains("aarch64"))
416                 return "arm64";
417
418             if (triple.Contains("arm"))
419                 return "arm";
420
421             if (triple.Contains("i686"))
422                 return "x86";
423
424             if (triple.Contains("x86_64"))
425                 return "x86_64";
426
427             throw  new Exception("Unknown architecture from triple: " + triple);
428         }
429
430         static string GetXcodeToolchainPath()
431         {
432             var toolchains = Directory.EnumerateDirectories("/Applications", "Xcode*")
433                 .ToList();
434             toolchains.Sort();
435
436             var toolchainPath = toolchains.LastOrDefault();
437             if (toolchainPath == null)
438                 throw new Exception("Could not find a valid Xcode SDK");
439
440             return toolchainPath;
441         }
442
443         static string GetXcodeBuiltinIncludesFolder()
444         {
445             var toolchainPath = GetXcodeToolchainPath();
446
447             var toolchains = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
448                 "Contents/Developer/Toolchains")).ToList();
449             toolchains.Sort();
450
451             toolchainPath = toolchains.LastOrDefault();
452             if (toolchainPath == null)
453                 throw new Exception("Could not find a valid Xcode toolchain");
454
455             var includePaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
456                 "usr/lib/clang")).ToList();
457             var includePath = includePaths.LastOrDefault();
458
459             if (includePath == null)
460                 throw new Exception("Could not find a valid Clang include folder");
461
462             return Path.Combine(includePath, "include");
463         }
464
465         static string GetXcodeiOSIncludesFolder()
466         {
467             var toolchainPath = GetXcodeToolchainPath();
468
469             var sdkPaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
470                 "Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs")).ToList();
471             var sdkPath = sdkPaths.LastOrDefault();
472
473             if (sdkPath == null)
474                 throw new Exception("Could not find a valid iPhone SDK");
475
476             return Path.Combine(sdkPath, "usr/include");
477         }
478
479         static string GetXcodeWatchOSIncludesFolder()
480         {
481             var toolchainPath = GetXcodeToolchainPath();
482
483             var sdkPaths = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
484                 "Contents/Developer/Platforms/WatchOS.platform/Developer/SDKs")).ToList();
485             var sdkPath = sdkPaths.LastOrDefault();
486
487             if (sdkPath == null)
488                 throw new Exception("Could not find a valid WatchOS SDK");
489
490             return Path.Combine(sdkPath, "usr/include");
491         }
492
493         static void SetupXcode(Driver driver, Target target)
494         {
495             var parserOptions = driver.ParserOptions;
496
497             var builtinsPath = GetXcodeBuiltinIncludesFolder();
498             string includePath;
499
500             switch (target.Platform) {
501             case TargetPlatform.iOS:
502                 includePath = GetXcodeiOSIncludesFolder();
503                 break;
504             case TargetPlatform.WatchOS:
505                 includePath = GetXcodeWatchOSIncludesFolder();
506                 break;
507             default:
508                 throw new ArgumentOutOfRangeException ();
509             }
510
511             parserOptions.AddSystemIncludeDirs(builtinsPath);
512             parserOptions.AddSystemIncludeDirs(includePath);
513
514             parserOptions.NoBuiltinIncludes = true;
515             parserOptions.NoStandardIncludes = true;
516             parserOptions.TargetTriple = target.Triple;
517         }
518
519         static string GetAndroidHostToolchainPath()
520         {
521             var androidNdkPath = GetAndroidNdkPath ();
522             var toolchains = Directory.EnumerateDirectories(
523                 Path.Combine(androidNdkPath, "toolchains"), "llvm*").ToList();
524             toolchains.Sort();
525
526             var toolchainPath = toolchains.LastOrDefault();
527             if (toolchainPath == null)
528                 throw new Exception("Could not find a valid NDK host toolchain");
529
530             toolchains = Directory.EnumerateDirectories(Path.Combine(toolchainPath,
531                 "prebuilt")).ToList();
532             toolchains.Sort();
533
534             toolchainPath = toolchains.LastOrDefault();
535             if (toolchainPath == null)
536                 throw new Exception("Could not find a valid NDK host toolchain");
537
538             return toolchainPath;
539         }
540
541         static string GetAndroidBuiltinIncludesFolder()
542         {
543             var toolchainPath = GetAndroidHostToolchainPath();
544
545             string clangToolchainPath = Path.Combine(toolchainPath, "lib64", "clang");
546             if (!Directory.Exists (clangToolchainPath))
547                 clangToolchainPath = Path.Combine(toolchainPath, "lib", "clang");
548
549             string includePath = null;
550             if (Directory.Exists (clangToolchainPath)) {
551                 var includePaths = Directory.EnumerateDirectories(clangToolchainPath).ToList();
552                 includePath = includePaths.LastOrDefault();
553             }
554             if (includePath == null)
555                 throw new Exception("Could not find a valid Clang include folder");
556
557             return Path.Combine(includePath, "include");
558         }
559
560         static void SetupAndroidNDK(Driver driver, Target target)
561         {
562             var options = driver.Options;
563             var parserOptions = driver.ParserOptions;
564
565             var builtinsPath = GetAndroidBuiltinIncludesFolder();
566             parserOptions.AddSystemIncludeDirs(builtinsPath);
567
568             var androidNdkRoot = GetAndroidNdkPath ();
569             const int androidNdkApiLevel = 21;
570
571             var toolchainPath = Path.Combine(androidNdkRoot, "platforms",
572                 "android-" + androidNdkApiLevel, "arch-" + GetArchFromTriple(target.Triple),
573                 "usr", "include");
574             parserOptions.AddSystemIncludeDirs(toolchainPath);
575
576             parserOptions.NoBuiltinIncludes = true;
577             parserOptions.NoStandardIncludes = true;
578             parserOptions.TargetTriple = target.Triple;
579         }
580
581         static uint GetTypeAlign(ParserTargetInfo target, ParserIntType type)
582         {
583             switch (type)
584             {
585                 case ParserIntType.SignedChar:
586                 case ParserIntType.UnsignedChar:
587                     return target.CharAlign;
588                 case ParserIntType.SignedShort:
589                 case ParserIntType.UnsignedShort:
590                     return target.ShortAlign;
591                 case ParserIntType.SignedInt:
592                 case ParserIntType.UnsignedInt:
593                     return target.IntAlign;
594                 case ParserIntType.SignedLong:
595                 case ParserIntType.UnsignedLong:
596                     return target.LongAlign;
597                 case ParserIntType.SignedLongLong:
598                 case ParserIntType.UnsignedLongLong:
599                     return target.LongLongAlign;
600                 default:
601                     throw new Exception("Type has no alignment");
602             }
603         }
604
605         static uint GetTypeSize(ParserTargetInfo target, ParserIntType type)
606         {
607             switch (type)
608             {
609                 case ParserIntType.SignedChar:
610                 case ParserIntType.UnsignedChar:
611                     return target.CharWidth;
612                 case ParserIntType.SignedShort:
613                 case ParserIntType.UnsignedShort:
614                     return target.ShortWidth;
615                 case ParserIntType.SignedInt:
616                 case ParserIntType.UnsignedInt:
617                     return target.IntWidth;
618                 case ParserIntType.SignedLong:
619                 case ParserIntType.UnsignedLong:
620                     return target.LongWidth;
621                 case ParserIntType.SignedLongLong:
622                 case ParserIntType.UnsignedLongLong:
623                     return target.LongLongWidth;
624                 default:
625                     throw new Exception("Type has no size");
626             }
627         }
628
629         static string GetTargetPlatformDefine(TargetPlatform target)
630         {
631             switch (target) {
632             case TargetPlatform.Android:
633                 return "TARGET_ANDROID";
634             case TargetPlatform.iOS:
635                 return "TARGET_IOS";
636             case TargetPlatform.WatchOS:
637                 return "TARGET_WATCHOS";
638             default:
639                 throw new ArgumentOutOfRangeException ();
640             }
641         }
642
643         static void Dump(ASTContext ctx, ParserTargetInfo targetInfo, Target target)
644         {
645             var targetFile = target.Triple;
646
647             if (!string.IsNullOrEmpty (OutputDir))
648                 targetFile = Path.Combine (OutputDir, targetFile);
649
650             targetFile += ".h";
651
652             using (var writer = new StreamWriter(targetFile))
653             //using (var writer = Console.Out)
654             {
655                 writer.WriteLine("#ifndef USED_CROSS_COMPILER_OFFSETS");
656                 writer.WriteLine("#ifdef {0}", target.Defines[0]);
657                 writer.WriteLine ("#ifdef {0}", GetTargetPlatformDefine (target.Platform));
658                 writer.WriteLine("#ifndef HAVE_BOEHM_GC");
659                 writer.WriteLine("#define HAS_CROSS_COMPILER_OFFSETS");
660                 writer.WriteLine("#if defined (USE_CROSS_COMPILE_OFFSETS) || defined (MONO_CROSS_COMPILE)");
661                 writer.WriteLine("#if !defined (DISABLE_METADATA_OFFSETS)");
662                 writer.WriteLine("#define USED_CROSS_COMPILER_OFFSETS");
663
664                 DumpAligns(writer, targetInfo);
665                 DumpSizes(writer, targetInfo);
666                 DumpMetadataOffsets(writer, ctx, target);
667
668                 writer.WriteLine("#endif //disable metadata check");
669
670                 DumpJITOffsets(writer, ctx);
671
672                 writer.WriteLine("#endif //cross compiler checks");
673                 writer.WriteLine("#endif //gc check");
674                 writer.WriteLine("#endif //os check");
675                 writer.WriteLine("#endif //arch check");
676                 writer.WriteLine("#endif //USED_CROSS_COMPILER_OFFSETS check");
677             }
678
679             Console.WriteLine("Generated offsets file: {0}", targetFile);
680         }
681
682         static void DumpAligns(TextWriter writer, ParserTargetInfo target)
683         {
684             var aligns = new[]
685             {
686                 new { Name = "gint8", Align = target.CharAlign},
687                 new { Name = "gint16", Align = target.ShortAlign},
688                 new { Name = "gint32", Align = target.IntAlign},
689                 new { Name = "gint64", Align = GetTypeAlign(target, target.Int64Type)},
690                 new { Name = "float", Align = target.FloatAlign},
691                 new { Name = "double", Align = target.DoubleAlign},
692                 new { Name = "gpointer", Align = GetTypeAlign(target, target.IntPtrType)},
693             };
694
695             // Write the alignment info for the basic types.
696             foreach (var align in aligns)
697                 writer.WriteLine("DECL_ALIGN2({0},{1})", align.Name, align.Align / 8);
698         }
699
700         static void DumpSizes(TextWriter writer, ParserTargetInfo target)
701         {
702             var sizes = new[]
703             {
704                 new { Name = "gint8", Size = target.CharWidth},
705                 new { Name = "gint16", Size = target.ShortWidth},
706                 new { Name = "gint32", Size = target.IntWidth},
707                 new { Name = "gint64", Size = GetTypeSize(target, target.Int64Type)},
708                 new { Name = "float", Size = target.FloatWidth},
709                 new { Name = "double", Size = target.DoubleWidth},
710                 new { Name = "gpointer", Size = GetTypeSize(target, target.IntPtrType)},
711             };
712
713             // Write the size info for the basic types.
714             foreach (var size in sizes)
715                 writer.WriteLine("DECL_SIZE2({0},{1})", size.Name, size.Size / 8);
716         }
717
718         static Class GetClassFromTypedef(ITypedDecl typedef)
719         {
720             var type = typedef.Type.Desugar() as TagType;
721             if (type == null)
722                 return null;
723
724             var @class = type.Declaration as Class;
725
726             return @class.IsIncomplete ?
727                 (@class.CompleteDeclaration as Class) : @class; 
728         }
729
730         static void DumpClasses(TextWriter writer, ASTContext ctx, IEnumerable<string> types,
731             bool optional = false)
732         {
733             foreach (var @struct in types)
734             {
735                 var @class = ctx.FindCompleteClass(@struct);
736                 if (@class == null)
737                     @class = ctx.FindCompleteClass("_" + @struct);
738
739                 if (@class == null)
740                 {
741                     var typedef = ctx.FindTypedef(@struct).FirstOrDefault(
742                         decl => !decl.IsIncomplete);
743
744                     if (typedef != null)
745                         @class = GetClassFromTypedef(typedef);
746                 }
747
748                 if (@class == null && optional)
749                     continue;
750
751                 if (@class == null)
752                     throw new Exception("Expected to find struct definition for " + @struct);
753
754                 DumpStruct(writer, @class);
755             }
756         }
757
758         static void DumpMetadataOffsets(TextWriter writer, ASTContext ctx, Target target)
759         {
760             var types = new List<string>
761             {
762                 "MonoObject",
763                 "MonoObjectHandlePayload",
764                 "MonoClass",
765                 "MonoVTable",
766                 "MonoDelegate",
767                 "MonoInternalThread",
768                 "MonoMulticastDelegate",
769                 "MonoTransparentProxy",
770                 "MonoRealProxy",
771                 "MonoRemoteClass",
772                 "MonoArray",
773                 "MonoArrayBounds",
774                 "MonoSafeHandle",
775                 "MonoHandleRef",
776                 "MonoComInteropProxy",
777                 "MonoString",
778                 "MonoException",
779                 "MonoTypedRef",
780                 "MonoThreadsSync",
781                 "SgenThreadInfo",
782                 "SgenClientThreadInfo"
783             };
784
785             DumpClasses(writer, ctx, types);
786         }
787
788         static void DumpJITOffsets(TextWriter writer, ASTContext ctx)
789         {
790             writer.WriteLine("#ifndef DISABLE_JIT_OFFSETS");
791             writer.WriteLine("#define USED_CROSS_COMPILER_OFFSETS");
792
793             var types = new[]
794             {
795                 "MonoLMF",
796                 "MonoMethodRuntimeGenericContext",
797                 "MonoJitTlsData",
798                 "MonoGSharedVtMethodRuntimeInfo",
799                 "MonoContinuation",
800                 "MonoContext",
801                 "MonoDelegateTrampInfo",
802             };
803
804             DumpClasses(writer, ctx, types);
805
806             var optionalTypes = new[]
807             {
808                 "GSharedVtCallInfo",
809                 "SeqPointInfo",
810                 "DynCallArgs", 
811                 "MonoLMFTramp",
812                 "InterpMethodArguments",
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 }