Cursor.ChildVisitResult HeaderVisitor(Cursor cursor, Cursor parent) { string filename = cursor.Extent.Start.File.Name.Replace('\\', '/'); // Do not visit any included header if (!filename.Equals(_context.HeaderFilename)) return Cursor.ChildVisitResult.Continue; headerQueue.Remove(filename); // Have we visited this header already? HeaderDefinition header; if (!project.HeaderDefinitions.TryGetValue(filename, out header)) { // No, define a new one header = new HeaderDefinition(filename); project.HeaderDefinitions[filename] = header; } _context.Header = header; if (cursor.IsDefinition) { switch (cursor.Kind) { case CursorKind.ClassDecl: case CursorKind.ClassTemplate: case CursorKind.EnumDecl: case CursorKind.StructDecl: ParseClassCursor(cursor); break; case CursorKind.TypedefDecl: ParseTypedefCursor(cursor); break; case CursorKind.Namespace: _context.Namespace = cursor.Spelling; cursor.VisitChildren(HeaderVisitor); _context.Namespace = ""; break; } } return Cursor.ChildVisitResult.Continue; }
void ParseClassCursor(Cursor cursor) { string className = cursor.Spelling; string fullyQualifiedName = TypeRefDefinition.GetFullyQualifiedName(cursor); ClassDefinition @class; if (project.ClassDefinitions.TryGetValue(fullyQualifiedName, out @class)) { if (@class.IsParsed) return; @class.Parent = _context.Class; } else { switch(cursor.Kind) { case CursorKind.ClassTemplate: @class = new ClassTemplateDefinition(className, _context.Header, _context.Class); break; case CursorKind.EnumDecl: @class = new EnumDefinition(className, _context.Header, _context.Class); break; default: @class = new ClassDefinition(className, _context.Header, _context.Class); break; } @class.NamespaceName = _context.Namespace; if (@class.FullyQualifiedName != fullyQualifiedName) { Console.WriteLine("Parsing error at " + fullyQualifiedName); } project.ClassDefinitions[fullyQualifiedName] = @class; } _context.Class = @class; _context.Class.IsParsed = true; // Unnamed struct escapes to the surrounding scope if (!(string.IsNullOrEmpty(className) && cursor.Kind == CursorKind.StructDecl)) { if (_context.Class.Parent != null) { _context.Class.Parent.NestedClasses.Add(_context.Class); } else { _context.Header.Classes.Add(_context.Class); } } AccessSpecifier parentMemberAccess = _context.MemberAccess; // Default class/struct access specifier switch (cursor.Kind) { case CursorKind.ClassDecl: _context.MemberAccess = AccessSpecifier.Private; break; case CursorKind.StructDecl: _context.Class.IsStruct = true; _context.MemberAccess = AccessSpecifier.Public; break; case CursorKind.ClassTemplate: if (cursor.TemplateCursorKind != CursorKind.ClassDecl) { _context.MemberAccess = AccessSpecifier.Private; } else { _context.MemberAccess = AccessSpecifier.Public; } break; case CursorKind.TypedefDecl: var underlying = cursor.TypedefDeclUnderlyingType.Canonical; if (underlying.TypeKind == ClangSharp.Type.Kind.Pointer && underlying.Pointee.TypeKind == ClangSharp.Type.Kind.FunctionProto) { _context.Class.IsFunctionProto = true; } break; } if (cursor.Kind == CursorKind.EnumDecl) { var @enum = _context.Class as EnumDefinition; foreach (var constantDecl in cursor.Children .Where(c => c.Kind == CursorKind.EnumConstantDecl)) { string valueSpelling = ""; var value = constantDecl.Children.FirstOrDefault(); if (value != null) { var valueTokens = _context.TranslationUnit.Tokenize(value.Extent) .Where(t => t.Kind != TokenKind.Comment && !t.Spelling.Equals(",") && !t.Spelling.Equals("}")); valueSpelling = string.Join("", valueTokens.Select(t => t.Spelling)); } @enum.EnumConstants.Add(new EnumConstant(constantDecl.Spelling, valueSpelling)); } } else { cursor.VisitChildren(ClassVisitor); if (_context.Class.BaseClass == null) { // Clang doesn't give the base class if it's a template, // tokenize the class definition and extract the base template if it exists ParseTemplateBaseCursor(cursor); } } // Restore parent state _context.Class = _context.Class.Parent; _context.MemberAccess = parentMemberAccess; }
void ParseClassCursor(Cursor cursor) { string className = cursor.Spelling; string fullyQualifiedName; if (cursor.Type.TypeKind != ClangSharp.Type.Kind.Invalid) { fullyQualifiedName = TypeRefDefinition.GetFullyQualifiedName(cursor.Type); } else if (currentClass != null) { fullyQualifiedName = currentClass.FullName + "::" + className; } else { fullyQualifiedName = className; } if (ClassDefinitions.ContainsKey(fullyQualifiedName)) { return; } if (string.IsNullOrEmpty(className) && cursor.Kind == CursorKind.StructDecl && currentClass == null) { return; } if (currentClass != null) { currentClass = new ClassDefinition(className, currentClass); } else { currentClass = new ClassDefinition(className, currentHeader); } // Unnamed struct escapes to the surrounding scope if (!(string.IsNullOrEmpty(className) && cursor.Kind == CursorKind.StructDecl)) { ClassDefinitions.Add(fullyQualifiedName, currentClass); if (currentClass.Parent != null) { currentClass.Parent.Classes.Add(currentClass); } else { currentHeader.Classes.Add(currentClass); } } AccessSpecifier parentMemberAccess = currentMemberAccess; // Default class/struct access specifier if (cursor.Kind == CursorKind.ClassDecl) { currentMemberAccess = AccessSpecifier.Private; } else if (cursor.Kind == CursorKind.StructDecl) { currentClass.IsStruct = true; currentMemberAccess = AccessSpecifier.Public; } else if (cursor.Kind == CursorKind.ClassTemplate) { currentClass.IsTemplate = true; if (cursor.TemplateCursorKind != CursorKind.ClassDecl) { currentMemberAccess = AccessSpecifier.Private; } else { currentMemberAccess = AccessSpecifier.Public; } } if (cursor.Kind == CursorKind.EnumDecl) { currentEnum = new EnumDefinition(fullyQualifiedName, cursor.Spelling); currentHeader.Enums.Add(currentEnum); cursor.VisitChildren(EnumVisitor); if (currentClass != null) { // Enum wrapped in a struct currentClass.Enum = currentEnum; } currentEnum = null; } else if (cursor.Kind == CursorKind.TypedefDecl) { currentClass.IsTypedef = true; if (cursor.TypedefDeclUnderlyingType.Canonical.TypeKind != ClangSharp.Type.Kind.FunctionProto) { currentClass.TypedefUnderlyingType = new TypeRefDefinition(cursor.TypedefDeclUnderlyingType); } } else { cursor.VisitChildren(ClassVisitor); } // Restore parent state currentClass = currentClass.Parent; currentMemberAccess = parentMemberAccess; }
Cursor.ChildVisitResult HeaderVisitor(Cursor cursor, Cursor parent) { string filename = cursor.Extent.Start.File.Name.Replace('\\', '/'); if (!filename.StartsWith(src, StringComparison.OrdinalIgnoreCase)) { return Cursor.ChildVisitResult.Continue; } // Have we visited this header already? if (HeaderDefinitions.ContainsKey(filename)) { currentHeader = HeaderDefinitions[filename]; } else { // No, define a new one string relativeFilename = filename.Substring(src.Length); currentHeader = new HeaderDefinition(relativeFilename); HeaderDefinitions.Add(filename, currentHeader); headerQueue.Remove(filename); } if ((cursor.Kind == CursorKind.ClassDecl || cursor.Kind == CursorKind.StructDecl || cursor.Kind == CursorKind.ClassTemplate || cursor.Kind == CursorKind.TypedefDecl) && cursor.IsDefinition) { ParseClassCursor(cursor); } else if (cursor.Kind == CursorKind.EnumDecl) { if (!currentHeader.Enums.Any(x => x.Name.Equals(cursor.Spelling))) { currentEnum = new EnumDefinition(cursor.Spelling, cursor.Spelling); currentHeader.Enums.Add(currentEnum); cursor.VisitChildren(EnumVisitor); currentEnum = null; } } else if (cursor.Kind == CursorKind.Namespace) { return Cursor.ChildVisitResult.Recurse; } return Cursor.ChildVisitResult.Continue; }
Cursor.ChildVisitResult ClassVisitor(Cursor cursor, Cursor parent) { if (cursor.Kind == CursorKind.CxxAccessSpecifier) { currentMemberAccess = cursor.AccessSpecifier; return Cursor.ChildVisitResult.Continue; } else if (cursor.Kind == CursorKind.CxxBaseSpecifier) { currentClass.BaseClass = new TypeRefDefinition(cursor.Type); return Cursor.ChildVisitResult.Continue; } if (currentMemberAccess != AccessSpecifier.Public) { return Cursor.ChildVisitResult.Continue; } if ((cursor.Kind == CursorKind.ClassDecl || cursor.Kind == CursorKind.StructDecl || cursor.Kind == CursorKind.ClassTemplate || cursor.Kind == CursorKind.TypedefDecl || cursor.Kind == CursorKind.EnumDecl) && cursor.IsDefinition) { ParseClassCursor(cursor); } else if (cursor.Kind == CursorKind.CxxMethod || cursor.Kind == CursorKind.Constructor) { string methodName = cursor.Spelling; if (excludedMethods.ContainsKey(methodName)) { return Cursor.ChildVisitResult.Continue; } currentMethod = new MethodDefinition(methodName, currentClass, cursor.NumArguments); currentMethod.ReturnType = new TypeRefDefinition(cursor.ResultType); currentMethod.IsStatic = cursor.IsStaticCxxMethod; currentMethod.IsConstructor = cursor.Kind == CursorKind.Constructor; if (cursor.IsVirtualCxxMethod) { currentMethod.IsVirtual = true; if (cursor.IsPureVirtualCxxMethod) { currentMethod.IsAbstract = true; currentClass.IsAbstract = true; } } // Check if the return type is a template cursor.VisitChildren(MethodTemplateTypeVisitor); // Parse arguments for (uint i = 0; i < cursor.NumArguments; i++) { Cursor arg = cursor.GetArgument(i); string parameterName = arg.Spelling; if (parameterName.Length == 0) { parameterName = "__unnamed" + i; } currentParameter = new ParameterDefinition(parameterName, new TypeRefDefinition(arg.Type)); currentMethod.Parameters[i] = currentParameter; arg.VisitChildren(MethodTemplateTypeVisitor); currentParameter = null; // Check if it's a const or optional parameter IEnumerable<Token> argTokens = currentTU.Tokenize(arg.Extent); foreach (Token token in argTokens) { if (token.Spelling.Equals("=")) { currentMethod.Parameters[i].IsOptional = true; } } } currentMethod = null; } else if (cursor.Kind == CursorKind.FieldDecl) { currentField = new FieldDefinition(cursor.Spelling, new TypeRefDefinition(cursor.Type), currentClass); currentFieldHasSpecializedParameter = false; cursor.VisitChildren(FieldTemplateTypeVisitor); currentField = null; } else if (cursor.Kind == CursorKind.UnionDecl) { return Cursor.ChildVisitResult.Recurse; } else { //Console.WriteLine(cursor.Spelling); } return Cursor.ChildVisitResult.Continue; }
Cursor.ChildVisitResult ClassVisitor(Cursor cursor, Cursor parent) { switch (cursor.Kind) { case CursorKind.CxxAccessSpecifier: _context.MemberAccess = cursor.AccessSpecifier; return Cursor.ChildVisitResult.Continue; case CursorKind.CxxBaseSpecifier: string baseName = TypeRefDefinition.GetFullyQualifiedName(cursor.Type); ClassDefinition baseClass; if (!project.ClassDefinitions.TryGetValue(baseName, out baseClass)) { Console.WriteLine("Base {0} for {1} not found! Missing header?", baseName, _context.Class.Name); return Cursor.ChildVisitResult.Continue; } _context.Class.BaseClass = baseClass; return Cursor.ChildVisitResult.Continue; case CursorKind.TemplateTypeParameter: var classTemplate = _context.Class as ClassTemplateDefinition; if (classTemplate.TemplateTypeParameters == null) { classTemplate.TemplateTypeParameters = new List<string>(); } classTemplate.TemplateTypeParameters.Add(cursor.Spelling); return Cursor.ChildVisitResult.Continue; } // We only care about public members if (_context.MemberAccess != AccessSpecifier.Public) { // And also private/protected virtual methods that override public abstract methods, // necessary for checking whether a class is abstract or not. if (cursor.IsPureVirtualCxxMethod || !cursor.IsVirtualCxxMethod) { return Cursor.ChildVisitResult.Continue; } } if ((cursor.Kind == CursorKind.ClassDecl || cursor.Kind == CursorKind.StructDecl || cursor.Kind == CursorKind.ClassTemplate || cursor.Kind == CursorKind.TypedefDecl || cursor.Kind == CursorKind.EnumDecl) && cursor.IsDefinition) { ParseClassCursor(cursor); } else if (cursor.Kind == CursorKind.CxxMethod || cursor.Kind == CursorKind.Constructor) { string methodName = cursor.Spelling; if (excludedMethods.Contains(methodName)) { return Cursor.ChildVisitResult.Continue; } var existingMethodsMatch = _context.Class.Methods.Where( m => !m.IsParsed && methodName.Equals(m.Name) && m.Parameters.Length == cursor.NumArguments); int existingCount = existingMethodsMatch.Count(); if (existingCount == 1) { // TODO: check method parameter types if given _context.Method = existingMethodsMatch.First(); } else if (existingCount >= 2) { Console.WriteLine("Ambiguous method in project: " + methodName); } if (_context.Method == null) { _context.Method = new MethodDefinition(methodName, _context.Class, cursor.NumArguments); } _context.Method.ReturnType = new TypeRefDefinition(cursor.ResultType); _context.Method.IsConstructor = cursor.Kind == CursorKind.Constructor; _context.Method.IsStatic = cursor.IsStaticCxxMethod; _context.Method.IsVirtual = cursor.IsVirtualCxxMethod; _context.Method.IsAbstract = cursor.IsPureVirtualCxxMethod; // Check if the return type is a template cursor.VisitChildren(MethodTemplateTypeVisitor); // Parse arguments for (uint i = 0; i < cursor.NumArguments; i++) { Cursor arg = cursor.GetArgument(i); string parameterName = arg.Spelling; if (parameterName.Length == 0) { parameterName = "__unnamed" + i; } if (_context.Method.Parameters[i] == null) { _context.Parameter = new ParameterDefinition(parameterName, new TypeRefDefinition(arg.Type)); _context.Method.Parameters[i] = _context.Parameter; } else { _context.Parameter = _context.Method.Parameters[i]; _context.Parameter.Type = new TypeRefDefinition(arg.Type); } arg.VisitChildren(MethodTemplateTypeVisitor); // Check for a default value (optional parameter) var argTokens = _context.TranslationUnit.Tokenize(arg.Extent); if (argTokens.Any(a => a.Spelling.Equals("="))) { _context.Parameter.IsOptional = true; } // Get marshalling direction if (_context.Parameter.Type.IsPointer || _context.Parameter.Type.IsReference) { if (_context.Parameter.MarshalDirection != MarshalDirection.Out && !_context.TranslationUnit.Tokenize(arg.Extent).Any(a => a.Spelling.Equals("const"))) { _context.Parameter.MarshalDirection = MarshalDirection.InOut; } } _context.Parameter = null; } // Discard any private/protected virtual method unless it // implements a public abstract method if (_context.MemberAccess != AccessSpecifier.Public) { if (_context.Method.Parent.BaseClass == null || !_context.Method.Parent.BaseClass.AbstractMethods.Contains(_context.Method)) { _context.Method.Parent.Methods.Remove(_context.Method); } } _context.Method.IsParsed = true; _context.Method = null; } else if (cursor.Kind == CursorKind.FieldDecl) { _context.Field = new FieldDefinition(cursor.Spelling, new TypeRefDefinition(cursor.Type), _context.Class); if (!cursor.Type.Declaration.SpecializedCursorTemplate.IsInvalid) { if (cursor.Children[0].Kind != CursorKind.TemplateRef) { throw new InvalidOperationException(); } if (cursor.Children.Count == 1) { string displayName = cursor.Type.Declaration.DisplayName; int typeStart = displayName.IndexOf('<') + 1; int typeEnd = displayName.LastIndexOf('>'); displayName = displayName.Substring(typeStart, typeEnd - typeStart); var specializationTypeRef = new TypeRefDefinition { IsBasic = true, Name = displayName }; _context.Field.Type.SpecializedTemplateType = specializationTypeRef; } if (cursor.Children.Count == 2) { if (cursor.Children[1].Type.TypeKind != ClangSharp.Type.Kind.Invalid) { _context.Field.Type.SpecializedTemplateType = new TypeRefDefinition(cursor.Children[1].Type); } else { // TODO } } } //cursor.VisitChildren(FieldTemplateTypeVisitor); _context.Field = null; } else if (cursor.Kind == CursorKind.UnionDecl) { return Cursor.ChildVisitResult.Recurse; } else { //Console.WriteLine(cursor.Spelling); } return Cursor.ChildVisitResult.Continue; }
void ParseClassCursor(Cursor cursor) { string className = cursor.Spelling; // Unnamed struct // A combined "typedef struct {}" definition is split into separate struct and typedef statements // where the struct is also a child of the typedef, so the struct can be skipped for now. if (string.IsNullOrEmpty(className) && cursor.Kind == CursorKind.StructDecl) { return; } string fullyQualifiedName = TypeRefDefinition.GetFullyQualifiedName(cursor); if (project.ClassDefinitions.ContainsKey(fullyQualifiedName)) { if (project.ClassDefinitions[fullyQualifiedName].IsParsed) { return; } var parent = _context.Class; _context.Class = project.ClassDefinitions[fullyQualifiedName]; _context.Class.Parent = parent; } else { if (cursor.Kind == CursorKind.ClassTemplate) { _context.Class = new ClassTemplateDefinition(className, _context.Header, _context.Class); } else if (cursor.Kind == CursorKind.EnumDecl) { _context.Class = new EnumDefinition(className, _context.Header, _context.Class); } else { _context.Class = new ClassDefinition(className, _context.Header, _context.Class); } _context.Class.NamespaceName = _context.Namespace; if (_context.Class.FullyQualifiedName != fullyQualifiedName) { // TODO } project.ClassDefinitions.Add(fullyQualifiedName, _context.Class); } _context.Class.IsParsed = true; // Unnamed struct escapes to the surrounding scope if (!(string.IsNullOrEmpty(className) && cursor.Kind == CursorKind.StructDecl)) { if (_context.Class.Parent != null) { _context.Class.Parent.Classes.Add(_context.Class); } else { _context.Header.Classes.Add(_context.Class); } } AccessSpecifier parentMemberAccess = _context.MemberAccess; // Default class/struct access specifier if (cursor.Kind == CursorKind.ClassDecl) { _context.MemberAccess = AccessSpecifier.Private; } else if (cursor.Kind == CursorKind.StructDecl) { _context.Class.IsStruct = true; _context.MemberAccess = AccessSpecifier.Public; } else if (cursor.Kind == CursorKind.ClassTemplate) { if (cursor.TemplateCursorKind != CursorKind.ClassDecl) { _context.MemberAccess = AccessSpecifier.Private; } else { _context.MemberAccess = AccessSpecifier.Public; } } if (cursor.Kind == CursorKind.EnumDecl) { cursor.VisitChildren(EnumVisitor); } else if (cursor.Kind == CursorKind.TypedefDecl) { _context.Class.IsTypedef = true; if (cursor.TypedefDeclUnderlyingType.Canonical.TypeKind != ClangSharp.Type.Kind.FunctionProto) { _context.Class.TypedefUnderlyingType = new TypeRefDefinition(cursor.TypedefDeclUnderlyingType); } } else { cursor.VisitChildren(ClassVisitor); } // Restore parent state _context.Class = _context.Class.Parent; _context.MemberAccess = parentMemberAccess; }