1 /** 2 * Copyright: Copyright (c) 2012 Jacob Carlborg. All rights reserved. 3 * Authors: Jacob Carlborg 4 * Version: Initial created: may 1, 2012 5 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0) 6 */ 7 module dstep.translator.Record; 8 9 import std.algorithm.mutation; 10 11 import clang.c.Index; 12 import clang.Cursor; 13 import clang.Visitor; 14 import clang.Util; 15 16 import dstep.translator.Context; 17 import dstep.translator.Enum; 18 import dstep.translator.Declaration; 19 import dstep.translator.Output; 20 import dstep.translator.Translator; 21 import dstep.translator.Type; 22 23 string translateRecordTypeKeyword(in Cursor cursor) 24 { 25 if (cursor.kind == CXCursorKind.unionDecl) 26 return "union"; 27 else 28 return "struct"; 29 } 30 31 void translatePackedAttribute(Output output, Context context, Cursor cursor) 32 { 33 if (auto attribute = cursor.findChild(CXCursorKind.packedAttr)) 34 output.singleLine("align (1):"); 35 } 36 37 struct BitField 38 { 39 string type; 40 string field; 41 uint width; 42 } 43 44 BitField[] translateBitFields( 45 Context context, 46 Cursor[] cursors) 47 { 48 static void pad(ref BitField[] bitFields, uint totalWidth) 49 { 50 uint padding = 0; 51 52 for (uint size = 8; size <= 64; size *= 2) 53 { 54 if (totalWidth <= size) 55 { 56 padding = size - totalWidth; 57 break; 58 } 59 } 60 61 if (padding != 0) 62 bitFields ~= BitField("uint", "", padding); 63 } 64 65 BitField[] bitFields; 66 uint totalWidth = 0; 67 68 foreach (cursor; cursors) 69 { 70 auto width = cursor.bitFieldWidth(); 71 72 if (width == 0) 73 { 74 pad(bitFields, totalWidth); 75 totalWidth = 0; 76 } 77 else 78 { 79 bitFields ~= BitField( 80 translateType(context, cursor).makeString(), 81 cursor.spelling(), 82 width); 83 84 totalWidth += width; 85 } 86 } 87 88 pad(bitFields, totalWidth); 89 90 return bitFields; 91 } 92 93 void translateBitFields( 94 Output output, 95 Context context, 96 Cursor[] cursors) 97 { 98 import std.range; 99 100 auto bitFields = translateBitFields(context, cursors); 101 102 if (bitFields.length == 1) 103 { 104 auto bitField = bitFields.front; 105 106 output.singleLine( 107 `mixin(bitfields!(%s, "%s", %s));`, 108 bitField.type, 109 bitField.field, 110 bitField.width); 111 } 112 else 113 { 114 output.multiLine("mixin(bitfields!(") in { 115 foreach (index, bitField; enumerate(bitFields)) 116 { 117 output.singleLine( 118 `%s, "%s", %s%s`, 119 bitField.type, 120 bitField.field, 121 bitField.width, 122 index + 1 == bitFields.length ? "));" : ","); 123 } 124 }; 125 } 126 } 127 128 void translateRecordDef( 129 Output output, 130 Context context, 131 Cursor cursor, 132 bool keepUnnamed = false) 133 { 134 import std.algorithm; 135 import std.array; 136 import std.format; 137 138 context.markAsDefined(cursor); 139 140 auto canonical = cursor.canonical; 141 142 auto spelling = keepUnnamed ? "" : context.translateTagSpelling(cursor); 143 spelling = spelling == "" ? spelling : " " ~ spelling; 144 auto type = translateRecordTypeKeyword(cursor); 145 146 output.subscopeStrong(cursor.extent, "%s%s", type, spelling) in { 147 alias predicate = (a, b) => 148 a == b || 149 a.isBitField() && 150 b.isBitField(); 151 152 auto declarations = cursor.children 153 .filter!(cursor => cursor.isDeclaration)(); 154 155 if (declarations.any!(cursor => cursor.isBitField())) 156 output.singleLine("import std.bitmanip : bitfields;"); 157 158 translatePackedAttribute(output, context, cursor); 159 160 foreach (chunk; declarations.chunkBy!predicate()) 161 { 162 auto cursor = chunk.front; 163 164 if (cursor.isBitField) 165 { 166 version (D1) 167 { /* do nothing */ } 168 else 169 translateBitFields(output, context, chunk.array); 170 } 171 else 172 { 173 switch (cursor.kind) 174 { 175 case CXCursorKind.fieldDecl: 176 output.flushLocation(cursor); 177 178 auto undecorated = 179 cursor.type.kind == CXTypeKind.elaborated ? 180 cursor.type.named.undecorated : 181 cursor.type.undecorated; 182 183 auto declaration = undecorated.declaration; 184 185 if (undecorated.declaration.isValid && 186 !context.alreadyDefined(declaration) && 187 !declaration.isGlobalLexically) 188 { 189 context.translator.translate(output, declaration); 190 } 191 192 translateVariable(output, context, cursor); 193 194 break; 195 196 case CXCursorKind.unionDecl: 197 case CXCursorKind.structDecl: 198 if (cursor.type.isAnonymous) 199 translateAnonymousRecord(output, context, cursor); 200 201 break; 202 203 case CXCursorKind.enumDecl: 204 translateEnum(output, context, cursor); 205 break; 206 207 default: break; 208 } 209 } 210 } 211 }; 212 } 213 214 void translateRecordDecl(Output output, Context context, Cursor cursor) 215 { 216 context.markAsDefined(cursor); 217 218 auto spelling = context.translateTagSpelling(cursor); 219 spelling = spelling == "" ? spelling : " " ~ spelling; 220 auto type = translateRecordTypeKeyword(cursor); 221 output.singleLine(cursor.extent, "%s%s;", type, spelling); 222 } 223 224 void translateAnonymousRecord(Output output, Context context, Cursor cursor) 225 { 226 if (!variablesInParentScope(cursor)) 227 translateRecordDef(output, context, cursor, true); 228 } 229 230 bool shouldSkipRecord(Context context, Cursor cursor) 231 { 232 if (cursor.kind == CXCursorKind.structDecl || 233 cursor.kind == CXCursorKind.unionDecl) 234 { 235 auto typedefp = context.typedefParent(cursor.canonical); 236 auto spelling = typedefp.isValid && cursor.spelling == "" 237 ? typedefp.spelling 238 : cursor.spelling; 239 240 return context.options.skipSymbols.contains(spelling); 241 } 242 243 return false; 244 } 245 246 bool shouldSkipRecordDefinition(Context context, Cursor cursor) 247 { 248 if (cursor.kind == CXCursorKind.structDecl || 249 cursor.kind == CXCursorKind.unionDecl) 250 { 251 auto typedefp = context.typedefParent(cursor.canonical); 252 auto spelling = typedefp.isValid && cursor.spelling == "" 253 ? typedefp.spelling 254 : cursor.spelling; 255 256 return context.options.skipDefinitions.contains(spelling); 257 } 258 259 return false; 260 } 261 262 void translateRecord(Output output, Context context, Cursor record) 263 { 264 if (context.alreadyDefined(record.canonical)) 265 return; 266 267 bool skipdef = shouldSkipRecordDefinition(context, record); 268 269 if (record.isDefinition && !skipdef) 270 translateRecordDef(output, context, record); 271 else if (!context.isInsideTypedef(record) && (record.definition.isEmpty || skipdef)) 272 translateRecordDecl(output, context, record); 273 }