1 /** 2 Translate aggregates 3 */ 4 module dpp.cursor.aggregate; 5 6 import dpp.from; 7 import std.range: isInputRange; 8 9 10 string[] translateStruct(in from!"clang".Cursor cursor, 11 ref from!"dpp.runtime.context".Context context) 12 @safe 13 { 14 import clang: Cursor; 15 assert(cursor.kind == Cursor.Kind.StructDecl); 16 return translateAggregate(context, cursor, "struct"); 17 } 18 19 string[] translateClass(in from!"clang".Cursor cursor, 20 ref from!"dpp.runtime.context".Context context) 21 @safe 22 { 23 import clang: Cursor; 24 import std.typecons: Nullable, nullable; 25 import std.algorithm: map, filter; 26 import std.array: join; 27 28 assert(cursor.kind == Cursor.Kind.ClassDecl || cursor.kind == Cursor.Kind.ClassTemplate); 29 30 string translateTemplateParam(in Cursor cursor) { 31 import dpp.type: translate; 32 const maybeType = cursor.kind == Cursor.Kind.TemplateTypeParameter 33 ? "" 34 : translate(cursor.type, context) ~ " "; 35 return maybeType ~ cursor.spelling; 36 } 37 38 auto templateParams = cursor 39 .children 40 .filter!(a => a.kind == Cursor.Kind.TemplateTypeParameter || a.kind == Cursor.Kind.NonTypeTemplateParameter) 41 .map!translateTemplateParam 42 ; 43 44 const spelling = cursor.kind == Cursor.Kind.ClassTemplate 45 ? nullable(cursor.spelling ~ `(` ~ templateParams.join(", ") ~ `)`) 46 : Nullable!string(); 47 48 return translateAggregate(context, cursor, "class", "struct", spelling); 49 } 50 51 string[] translateUnion(in from!"clang".Cursor cursor, 52 ref from!"dpp.runtime.context".Context context) 53 @safe 54 { 55 import clang: Cursor; 56 assert(cursor.kind == Cursor.Kind.UnionDecl); 57 return translateAggregate(context, cursor, "union"); 58 } 59 60 string[] translateEnum(in from!"clang".Cursor cursor, 61 ref from!"dpp.runtime.context".Context context) 62 @safe 63 { 64 import clang: Cursor; 65 import std.typecons: nullable; 66 67 assert(cursor.kind == Cursor.Kind.EnumDecl); 68 69 // Translate it twice so that C semantics are the same (global names) 70 // but also have a named version for optional type correctness and 71 // reflection capabilities. 72 // This means that `enum Foo { foo, bar }` in C will become: 73 // `enum Foo { foo, bar }` _and_ 74 // `enum foo = Foo.foo; enum bar = Foo.bar;` in D. 75 76 auto enumName = context.spellingOrNickname(cursor); 77 78 string[] lines; 79 foreach(member; cursor) { 80 if(!member.isDefinition) continue; 81 auto memName = member.spelling; 82 lines ~= `enum ` ~ memName ~ ` = ` ~ enumName ~ `.` ~ memName ~ `;`; 83 } 84 85 return 86 translateAggregate(context, cursor, "enum", nullable(enumName)) ~ 87 lines; 88 } 89 90 // not pure due to Cursor.opApply not being pure 91 string[] translateAggregate( 92 ref from!"dpp.runtime.context".Context context, 93 in from!"clang".Cursor cursor, 94 in string keyword, 95 in from!"std.typecons".Nullable!string spelling = from!"std.typecons".Nullable!string() 96 ) 97 @safe 98 { 99 return translateAggregate(context, cursor, keyword, keyword, spelling); 100 } 101 102 // not pure due to Cursor.opApply not being pure 103 string[] translateAggregate( 104 ref from!"dpp.runtime.context".Context context, 105 in from!"clang".Cursor cursor, 106 in string cKeyword, 107 in string dKeyword, 108 in from!"std.typecons".Nullable!string spelling = from!"std.typecons".Nullable!string() 109 ) 110 @safe 111 { 112 import dpp.cursor.translation: translate; 113 import clang: Cursor; 114 import std.algorithm: map, any; 115 import std.array: array; 116 import std.conv: text; 117 118 // remember all aggregate declarations 119 context.rememberAggregate(cursor); 120 121 const name = spelling.isNull ? context.spellingOrNickname(cursor) : spelling.get; 122 const firstLine = dKeyword ~ ` ` ~ name; 123 124 if(!cursor.isDefinition) return [firstLine ~ `;`]; 125 126 string[] lines; 127 lines ~= firstLine; 128 lines ~= `{`; 129 130 if(cKeyword == "class") lines ~= "private:"; 131 132 if(cursor.children.any!(a => a.isBitField)) { 133 // The align(4) is to mimic C. There, `struct Foo { int f1: 2; int f2: 3}` 134 // would have sizeof 4, where as the corresponding bit fields in D would have 135 // size 1. So we correct here. See issue #7. 136 lines ~= [` import std.bitmanip: bitfields;`, ``, ` align(4):`]; 137 } 138 139 // if the last seen member was a bitfield 140 bool lastMemberWasBitField = false; 141 // the combined (summed) bitwidths of the bitfields members seen so far 142 int totalBitWidth = 0; 143 144 void finishBitFields() { 145 lines ~= text(` uint, "", `, padding(totalBitWidth)); 146 lines ~= ` ));`; 147 totalBitWidth = 0; 148 } 149 150 bool skipMember(in Cursor member) { 151 return 152 !member.isDefinition && 153 member.kind != Cursor.Kind.CXXMethod && 154 member.kind != Cursor.Kind.Constructor && 155 member.kind != Cursor.Kind.Destructor && 156 member.kind != Cursor.Kind.VarDecl && 157 member.kind != Cursor.Kind.CXXBaseSpecifier; 158 } 159 160 context.log("Children: ", cursor.children); 161 162 foreach(member; cursor.children) { 163 164 if(member.kind == Cursor.Kind.PackedAttr) { 165 lines ~= "align(1):"; 166 continue; 167 } 168 169 if(member.isBitField && !lastMemberWasBitField) 170 lines ~= ` mixin(bitfields!(`; 171 172 if(!member.isBitField && lastMemberWasBitField) finishBitFields; 173 174 if(skipMember(member)) continue; 175 176 lines ~= translate(member, context).map!(a => " " ~ a).array; 177 178 lastMemberWasBitField = member.isBitField; 179 if(member.isBitField) totalBitWidth += member.bitWidth; 180 } 181 182 if(lastMemberWasBitField) finishBitFields; 183 184 lines ~= `}`; 185 186 return lines; 187 } 188 189 private int padding(in int totalBitWidth) @safe @nogc pure nothrow { 190 for(int powerOfTwo = 8; powerOfTwo < 64; powerOfTwo *= 2) { 191 if(powerOfTwo > totalBitWidth) return powerOfTwo - totalBitWidth; 192 } 193 194 assert(0); 195 } 196 197 198 string[] translateField(in from!"clang".Cursor field, 199 ref from!"dpp.runtime.context".Context context) 200 @safe 201 { 202 203 import dpp.cursor.dlang: maybeRename; 204 import dpp.type: translate; 205 import clang: Cursor, Type; 206 import std.conv: text; 207 import std.typecons: No; 208 import std.array: replace; 209 210 assert(field.kind == Cursor.Kind.FieldDecl, text("Field of wrong kind: ", field)); 211 212 // The field could be a pointer to an undeclared struct or a function pointer with parameter 213 // or return types that are a pointer to an undeclared struct. We have to remember these 214 // so as to be able to declare the structs for D consumption after the fact. 215 if(field.type.kind == Type.Kind.Pointer) maybeRememberStructsFromType(field.type, context); 216 217 // Remember the field name in case it ends up clashing with a type. 218 context.rememberField(field.spelling); 219 220 const type = translate(field.type, context, No.translatingFunction); 221 222 return field.isBitField 223 ? [text(" ", type, `, "`, maybeRename(field, context), `", `, field.bitWidth, `,`)] 224 : [text(type, " ", maybeRename(field, context), ";")]; 225 } 226 227 228 private void maybeRememberStructsFromType(in from!"clang".Type type, 229 ref from!"dpp.runtime.context".Context context) 230 @safe pure 231 { 232 import clang: Type; 233 import std.range: only, chain; 234 235 const pointeeType = type.pointee.canonical; 236 const isFunction = 237 pointeeType.kind == Type.Kind.FunctionProto || 238 pointeeType.kind == Type.Kind.FunctionNoProto; 239 240 if(pointeeType.kind == Type.Kind.Record) 241 maybeRememberStructs([type], context); 242 else if(isFunction) 243 maybeRememberStructs(chain(only(pointeeType.returnType), pointeeType.paramTypes), 244 context); 245 } 246 247 void maybeRememberStructs(R)(R types, ref from!"dpp.runtime.context".Context context) 248 @safe pure if(isInputRange!R) 249 { 250 import dpp.type: translate; 251 import clang: Type; 252 import std.algorithm: map, filter; 253 254 auto structTypes = types 255 .filter!(a => a.kind == Type.Kind.Pointer && a.pointee.canonical.kind == Type.Kind.Record) 256 .map!(a => a.pointee.canonical); 257 258 void rememberStruct(in Type pointeeCanonicalType) { 259 const translatedType = translate(pointeeCanonicalType, context); 260 // const becomes a problem if we have to define a struct at the end of all translations. 261 // See it.compile.projects.nv_alloc_ops 262 enum constPrefix = "const("; 263 const cleanedType = pointeeCanonicalType.isConstQualified 264 ? translatedType[constPrefix.length .. $-1] // unpack from const(T) 265 : translatedType; 266 267 if(cleanedType != "va_list") 268 context.rememberFieldStruct(cleanedType); 269 } 270 271 foreach(structType; structTypes) 272 rememberStruct(structType); 273 } 274 275 // if the cursor is an aggregate in C, i.e. struct, union or enum 276 package bool isAggregateC(in from!"clang".Cursor cursor) @safe @nogc pure nothrow { 277 import clang: Cursor; 278 return 279 cursor.kind == Cursor.Kind.StructDecl || 280 cursor.kind == Cursor.Kind.UnionDecl || 281 cursor.kind == Cursor.Kind.EnumDecl; 282 }