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 }