public TranslationUnit WriteToEmpty(string source) { var translationUnit = Index.ParseTranslationUnit( TestFiles.Empty, new[] { "-std=c++11" }, new[] { new UnsavedFile(TestFiles.Empty, source) }, TranslationUnitCreationOptions.DetailedPreprocessingRecord); var set = DiagnosticSet.FromTranslationUnit(translationUnit); if (set.GetNumDiagnostics() != 0) { Assert.Fail(set.GetDiagnostic(0).ToString()); } return(translationUnit); }
public void GetOneDiagnosticInTranslationUnit() { string source = "#endif"; using (var empty = disposables.Index.ParseTranslationUnit( TestFiles.Empty, null, new[] { new UnsavedFile(TestFiles.Empty, source) }, TranslationUnitCreationOptions.DetailedPreprocessingRecord)) { var set = DiagnosticSet.FromTranslationUnit(empty); Assert.AreEqual <uint>(1, set.GetNumDiagnostics()); Assert.IsNotNull(set.GetDiagnostic(0)); Assert.IsNull(set.GetDiagnostic(1)); var diagnostic = set.GetDiagnostic(0); Assert.IsNull(diagnostic.GetChildDiagnostics()); Assert.AreEqual(DiagnosticSeverity.Error, diagnostic.GetSeverity()); } }
public void GenerateTypes(string libraryName = "imobiledevice") { string[] arguments = { // Use the C++ backend "-x", "c++", // Parse the doxygen comments "-Wdocumentation", // Target 32-bit OS "-m32" }; arguments = arguments.Concat(this.IncludeDirectories.Select(x => "-I" + x)).ToArray(); FunctionVisitor functionVisitor; using (var createIndex = new Index(false, true)) using (var translationUnit = createIndex.ParseTranslationUnit(this.InputFile, arguments)) { StringWriter errorWriter = new StringWriter(); var set = DiagnosticSet.FromTranslationUnit(translationUnit); var numDiagnostics = set.GetNumDiagnostics(); bool hasError = false; bool hasWarning = false; for (uint i = 0; i < numDiagnostics; ++i) { Diagnostic diagnostic = set.GetDiagnostic(i); var severity = diagnostic.GetSeverity(); switch (severity) { case DiagnosticSeverity.Error: case DiagnosticSeverity.Fatal: hasError = true; break; case DiagnosticSeverity.Warning: hasWarning = true; break; } var location = diagnostic.GetLocation(); var fileName = location.SourceFile; var line = location.Line; var message = diagnostic.GetSpelling(); errorWriter.WriteLine($"{severity}: {fileName}:{line} {message}"); } if (hasError) { throw new Exception(errorWriter.ToString()); } if (hasWarning) { // Dump the warnings to the console output. Console.WriteLine(errorWriter.ToString()); } // Generate the marhaler types for string arrays (char **) var arrayMarshalerGenerator = new ArrayMarshalerGenerator(this); arrayMarshalerGenerator.Generate(); // Creates enums var enumVisitor = new EnumVisitor(this); var realEnumVisitor = new DelegatingCursorVisitor(enumVisitor.Visit); var cursor = translationUnit.GetCursor(); realEnumVisitor.VisitChildren(cursor); // Creates structs var structVisitor = new StructVisitor(this); var realStructVisitor = new DelegatingCursorVisitor(structVisitor.Visit); realStructVisitor.VisitChildren(translationUnit.GetCursor()); // Creates safe handles & delegates var typeDefVisitor = new TypeDefVisitor(this); var realTypeDefVisitor = new DelegatingCursorVisitor(typeDefVisitor.Visit); realTypeDefVisitor.VisitChildren(translationUnit.GetCursor()); // Creates functions in a NativeMethods class functionVisitor = new FunctionVisitor(this, libraryName); var realFunctionVisitor = new DelegatingCursorVisitor(functionVisitor.Visit); realFunctionVisitor.VisitChildren(translationUnit.GetCursor()); createIndex.Dispose(); } // Update the SafeHandle to call the _free method var handles = this.Types.Where(t => t.Name.EndsWith("Handle")); foreach (var handle in handles) { var freeMethod = functionVisitor.NativeMethods.Members .OfType <CodeMemberMethod>() .Where(m => (m.Name.EndsWith("_free") || m.Name.EndsWith("_disconnect")) && m.Parameters.Count == 1 && m.Parameters[0].Type.BaseType == handle.Name) .SingleOrDefault(); if (freeMethod == null) { continue; } var type = (HandleType)((NustacheGeneratedType)handle).Type; type.ReleaseMethodName = freeMethod.Name; type.ReleaseMethodReturnsValue = freeMethod.ReturnType.BaseType != "System.Void"; // Directly pass the IntPtr, becuase the handle itself will already be in the 'closed' state // when this method is called. freeMethod.Parameters[0].Type = new CodeTypeReference(typeof(IntPtr)); freeMethod.Parameters[0].Direction = FieldDirection.In; } // Extract the API interface and class, as well as the Exception class. Used for DI. ApiExtractor extractor = new ApiExtractor(this, functionVisitor); extractor.Generate(); // Add the 'Error' extension IsError and ThrowOnError extension methods var extensionsExtractor = new ErrorExtensionExtractor(this, functionVisitor); extensionsExtractor.Generate(); // Patch the native methods to be compatible with .NET Core - basically, // do the marshalling ourselves NativeMethodOverloadGenerator.Generate(this); }
public void GenerateTypes() { string[] arguments = { // Use the C++ backend "-x", "c++", // Parse the doxygen comments "-Wdocumentation", }; arguments = arguments.Concat(this.IncludeDirectories.Select(x => "-I" + x)).ToArray(); using (var createIndex = new Index(false, true)) using (var translationUnit = createIndex.ParseTranslationUnit(this.InputFile, arguments)) { StringWriter errorWriter = new StringWriter(); var set = DiagnosticSet.FromTranslationUnit(translationUnit); var numDiagnostics = set.GetNumDiagnostics(); bool hasError = false; bool hasWarning = false; for (uint i = 0; i < numDiagnostics; ++i) { Diagnostic diagnostic = set.GetDiagnostic(i); var severity = diagnostic.GetSeverity(); switch (severity) { case DiagnosticSeverity.Error: case DiagnosticSeverity.Fatal: hasError = true; break; case DiagnosticSeverity.Warning: hasWarning = true; break; } var location = diagnostic.GetLocation(); var fileName = location.SourceFile; var line = location.Line; var message = diagnostic.GetSpelling(); errorWriter.WriteLine($"{severity}: {fileName}:{line} {message}"); } if (hasError) { throw new Exception(errorWriter.ToString()); } if (hasWarning) { // Dump the warnings to the console output. Console.WriteLine(errorWriter.ToString()); } // Creates enums var enumVisitor = new EnumVisitor(this); var realEnumVisitor = new DelegatingCursorVisitor(enumVisitor.Visit); realEnumVisitor.VisitChildren(translationUnit.GetCursor()); // Creates handles var typeDefVisitor = new TypeDefVisitor(this); var realTypeDefVisitor = new DelegatingCursorVisitor(typeDefVisitor.Visit); realTypeDefVisitor.VisitChildren(translationUnit.GetCursor()); // Creates C# methods for C functions var functionVisitor = new FunctionVisitor(this); var realFunctionVisitor = new DelegatingCursorVisitor(functionVisitor.Visit); realFunctionVisitor.VisitChildren(translationUnit.GetCursor()); // Creates C# strucs for C structs var structVisitor = new StructVisitor(this); var realStructVisitor = new DelegatingCursorVisitor(structVisitor.Visit); realStructVisitor.VisitChildren(translationUnit.GetCursor()); } }
public void Generate(string targetDirectory, string libraryName = "imobiledevice") { string[] arguments = { // Use the C++ backend "-x", "c++", // Parse the doxygen comments "-Wdocumentation", // Target 32-bit OS "-m32" }; arguments = arguments.Concat(this.IncludeDirectories.Select(x => "-I" + x)).ToArray(); FunctionVisitor functionVisitor; using (var createIndex = new Index(false, true)) using (var translationUnit = createIndex.ParseTranslationUnit(this.InputFile, arguments)) { StringWriter errorWriter = new StringWriter(); var set = DiagnosticSet.FromTranslationUnit(translationUnit); var numDiagnostics = set.GetNumDiagnostics(); bool hasError = false; bool hasWarning = false; for (uint i = 0; i < numDiagnostics; ++i) { Diagnostic diagnostic = set.GetDiagnostic(i); var severity = diagnostic.GetSeverity(); switch (severity) { case DiagnosticSeverity.Error: case DiagnosticSeverity.Fatal: hasError = true; break; case DiagnosticSeverity.Warning: hasWarning = true; break; } var location = diagnostic.GetLocation(); var fileName = location.SourceFile; var line = location.Line; var message = diagnostic.GetSpelling(); errorWriter.WriteLine($"{severity}: {fileName}:{line} {message}"); } if (hasError) { throw new Exception(errorWriter.ToString()); } if (hasWarning) { // Dump the warnings to the console output. Console.WriteLine(errorWriter.ToString()); } // Generate the marhaler types for string arrays (char **) var arrayMarshalerGenerator = new ArrayMarshalerGenerator(this); arrayMarshalerGenerator.Generate(); // Creates enums var enumVisitor = new EnumVisitor(this); var realEnumVisitor = new DelegatingCursorVisitor(enumVisitor.Visit); var cursor = translationUnit.GetCursor(); realEnumVisitor.VisitChildren(cursor); // Creates structs var structVisitor = new StructVisitor(this); var realStructVisitor = new DelegatingCursorVisitor(structVisitor.Visit); realStructVisitor.VisitChildren(translationUnit.GetCursor()); // Creates safe handles & delegates var typeDefVisitor = new TypeDefVisitor(this); var realTypeDefVisitor = new DelegatingCursorVisitor(typeDefVisitor.Visit); realTypeDefVisitor.VisitChildren(translationUnit.GetCursor()); // Creates functions in a NativeMethods class functionVisitor = new FunctionVisitor(this, libraryName); var realFunctionVisitor = new DelegatingCursorVisitor(functionVisitor.Visit); realFunctionVisitor.VisitChildren(translationUnit.GetCursor()); createIndex.Dispose(); } var moduleDirectory = Path.Combine(targetDirectory, this.Name); if (!Directory.Exists(moduleDirectory)) { Directory.CreateDirectory(moduleDirectory); } // Update the SafeHandle to call the _free method var handles = this.Types.Where(t => t.Name.EndsWith("Handle")); foreach (var handle in handles) { var freeMethod = functionVisitor.NativeMethods.Members .OfType <CodeMemberMethod>() .Where(m => (m.Name.EndsWith("_free") || m.Name.EndsWith("_disconnect")) && m.Parameters.Count == 1 && m.Parameters[0].Type.BaseType == handle.Name) .SingleOrDefault(); if (freeMethod == null) { continue; } // Directly pass the IntPtr, becuase the handle itself will already be in the 'closed' state // when this method is called. freeMethod.Parameters[0].Type = new CodeTypeReference(typeof(IntPtr)); freeMethod.Parameters[0].Direction = FieldDirection.In; var releaseMethod = handle.Members.OfType <CodeMemberMethod>().Single(m => m.Name == "ReleaseHandle"); // Sample statement: // System.Diagnostics.Debug.WriteLine("Releasing {0} {1}", this.GetType().Name, this.handle); // this.Api.Plist.plist_free(this.handle); // return true; releaseMethod.Statements.Clear(); // Trace the release call: // Debug.WriteLine("Releasing {0} {1}", this.GetType().Name, this.handle); releaseMethod.Statements.Add( new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodeTypeReferenceExpression(typeof(Debug)), "WriteLine"), new CodePrimitiveExpression("Releasing {0} {1} using {2}. This object was created at {3}"), new CodePropertyReferenceExpression( new CodeMethodInvokeExpression( new CodeThisReferenceExpression(), nameof(GetType)), "Name"), new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), "handle"), new CodePropertyReferenceExpression( new CodeThisReferenceExpression(), "Api"), new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), "creationStackTrace"))); var freeMethodInvokeExpression = new CodeMethodInvokeExpression( new CodeMethodReferenceExpression( new CodePropertyReferenceExpression( new CodePropertyReferenceExpression( new CodeThisReferenceExpression(), "Api"), this.Name), freeMethod.Name), new CodeFieldReferenceExpression( new CodeThisReferenceExpression(), "handle")); if (freeMethod.ReturnType.BaseType != "System.Void") { // If the free method returns a value, it's an error code, and we can make sure the value indicates // success. releaseMethod.Statements.Add( new CodeMethodReturnStatement( new CodeBinaryOperatorExpression( freeMethodInvokeExpression, CodeBinaryOperatorType.IdentityEquality, new CodePropertyReferenceExpression( new CodeTypeReferenceExpression($"{this.Name}Error"), "Success")))); } else { // If it does not, we always return true (which is a pitty, but this is how plist is implemented for now) // - on the other hand, in how many ways can free() really fail? :-) releaseMethod.Statements.Add(freeMethodInvokeExpression); releaseMethod.Statements.Add(new CodeMethodReturnStatement(new CodePrimitiveExpression(true))); } } // Extract the API interface and class, as well as the Exception class. Used for DI. ApiExtractor extractor = new ApiExtractor(this, functionVisitor); extractor.Generate(); // Add the 'Error' extension IsError and ThrowOnError extension methods var extensionsExtractor = new ErrorExtensionExtractor(this, functionVisitor); extensionsExtractor.Generate(); // Patch the native methods to be compatible with .NET Core - basically, // do the marshalling ourselves NativeMethodOverloadGenerator.Generate(this); // Write the files foreach (var declaration in this.Types) { if (declaration.Name.EndsWith("Private")) { continue; } // Generate the container unit CodeCompileUnit program = new CodeCompileUnit(); // Generate the namespace CodeNamespace ns = new CodeNamespace($"iMobileDevice.{this.Name}"); ns.Imports.Add(new CodeNamespaceImport("System.Runtime.InteropServices")); ns.Imports.Add(new CodeNamespaceImport("System.Diagnostics")); ns.Imports.Add(new CodeNamespaceImport("iMobileDevice.iDevice")); ns.Imports.Add(new CodeNamespaceImport("iMobileDevice.Lockdown")); ns.Imports.Add(new CodeNamespaceImport("iMobileDevice.Afc")); ns.Imports.Add(new CodeNamespaceImport("iMobileDevice.Plist")); ns.Types.Add(declaration); program.Namespaces.Add(ns); string suffix = string.Empty; if (declaration.UserData.Contains("FileNameSuffix")) { suffix = (string)declaration.UserData["FileNameSuffix"]; } string path = Path.Combine(moduleDirectory, $"{declaration.Name}{suffix}.cs"); using (var outFile = File.Open(path, FileMode.Create, FileAccess.Write, FileShare.None)) using (var fileWriter = new StreamWriter(outFile)) using (var indentedTextWriter = new CSharpTextWriter(fileWriter, " ")) { // Generate source code using the code provider. indentedTextWriter.Generate(ns); indentedTextWriter.Flush(); } // Add #if statements for code that doesn't work on .NET Core if (true) { string content = File.ReadAllText(path); content = content.Replace("#region !core", "#if !NETSTANDARD1_5"); content = content.Replace("#endregion", "#endif"); using (StringReader reader = new StringReader(content)) using (StreamWriter writer = new StreamWriter(path)) { while (reader.Peek() >= 0) { string line = reader.ReadLine(); if (line.Contains(nameof(SecurityPermissionAttribute)) || line.Contains(nameof(ReliabilityContractAttribute)) || line.Contains(nameof(SerializableAttribute))) { writer.WriteLine("#if !NETSTANDARD1_5"); writer.WriteLine(line); writer.WriteLine("#endif"); } else if (line.Contains("SerializationInfo info")) { writer.WriteLine("#if !NETSTANDARD1_5"); writer.WriteLine(line); writer.WriteLine(reader.ReadLine()); writer.WriteLine(reader.ReadLine()); writer.WriteLine(reader.ReadLine()); writer.WriteLine("#endif"); } else { writer.WriteLine(line); } } } } // Fix other CodeDOM shortcomings if (declaration.Name.EndsWith("NativeMethods") && string.IsNullOrEmpty(suffix)) { string content = File.ReadAllText(path); content = content.Replace("public abstract", "public static extern"); File.WriteAllText(path, content); } if (declaration.Name.EndsWith("Extensions")) { string content = File.ReadAllText(path); content = content.Replace("public class", "public static class"); File.WriteAllText(path, content); } } }