1 /**
2  * Copyright: Copyright (c) 2012 Jacob Carlborg. All rights reserved.
3  * Authors: Jacob Carlborg
4  * Version: Initial created: Jan 30, 2012
5  * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0)
6  */
7 module dstep.translator.Type;
8 
9 import std.conv;
10 import std..string;
11 import std.range;
12 import std.typecons: Nullable;
13 
14 import clang.c.Index;
15 import clang.Cursor;
16 import clang.Type;
17 import clang.Token: Token;
18 
19 import dstep.translator.Context;
20 import dstep.translator.IncludeHandler;
21 import dstep.translator.Translator;
22 import dstep.translator.Output;
23 
24 SourceNode translateType (Context context, Cursor cursor, bool rewriteIdToObjcObject = true, bool applyConst = true)
25 {
26     return translateType(context, cursor, cursor.type, rewriteIdToObjcObject, applyConst);
27 }
28 
29 SourceNode translateType (Context context, Cursor cursor, Type type, bool rewriteIdToObjcObject = true, bool applyConst = true)
30 in
31 {
32     assert(type.isValid);
33 }
34 body
35 {
36     SourceNode result;
37 
38     with (CXTypeKind)
39     {
40         if (type.kind == blockPointer || type.isFunctionPointerType)
41             result = translateFunctionPointerType(context, cursor, type.pointee.func);
42 
43         else if (type.isFunctionType)
44             result = translateFunctionPointerType(context, cursor, type.canonical.func);
45 
46         else if (type.kind == objCObjectPointer && !type.isObjCBuiltinType)
47             result = translateObjCObjectPointerType(context, cursor, type);
48 
49         else if (type.isWideCharType)
50             result = makeSourceNode("wchar");
51 
52         else if (type.isObjCIdType)
53             result = makeSourceNode(rewriteIdToObjcObject ? "ObjcObject" : "id");
54 
55         else
56             switch (type.kind)
57             {
58                 case pointer:
59                     return translatePointer(context, cursor, type, rewriteIdToObjcObject, applyConst);
60 
61                 case typedef_:
62                     result = translateTypedef(context, type).makeSourceNode();
63                     break;
64 
65                 case record:
66                 case enum_:
67                     result = makeSourceNode(context.translateTagSpelling(type.declaration));
68                     handleInclude(context, type);
69                     break;
70 
71                 case objCInterface:
72                     if (type.spelling.empty)
73                         result = makeSourceNode(context.getAnonymousName(type.declaration));
74                     else
75                         result = makeSourceNode(type.spelling);
76 
77                     handleInclude(context, type);
78                     break;
79 
80                 case constantArray:
81                 case incompleteArray:
82                     result = translateArray(
83                         context,
84                         cursor,
85                         type,
86                         rewriteIdToObjcObject,
87                         type.array.numDimensions - 1);
88                     break;
89 
90                 case unexposed:
91                     result = translateUnexposed(
92                         context,
93                         type,
94                         rewriteIdToObjcObject);
95                     break;
96 
97                 case elaborated:
98                     result = translateElaborated(
99                         context,
100                         cursor,
101                         type,
102                         rewriteIdToObjcObject);
103                     break;
104 
105                 case complex:
106                     result = translateComplex(type).makeSourceNode();
107                     break;
108 
109                 default:
110                     result = translateType(
111                         context,
112                         type.kind,
113                         rewriteIdToObjcObject)
114                         .makeSourceNode();
115             }
116     }
117 
118     version (D1)
119     {
120         // ignore const
121     }
122     else
123     {
124         if (applyConst && type.isConst)
125             result = result.prefixWith("const ");
126     }
127 
128     return result;
129 }
130 
131 SourceNode translateElaborated (Context context, Cursor cursor, Type type, bool rewriteIdToObjcObject = true, bool applyConst = true)
132 {
133     auto named = type.named();
134 
135     if (named.kind == CXTypeKind.record || named.kind == CXTypeKind.enum_)
136     {
137         auto result = context.translateTagSpelling(named.declaration);
138         handleInclude(context, type);
139         return result.makeSourceNode();
140     }
141     else
142     {
143         return translateType(
144             context,
145             cursor,
146             type.named,
147             rewriteIdToObjcObject);
148     }
149 }
150 
151 string translateSelector (string str, bool fullName = false, bool translateIdentifier = true)
152 {
153     import std.array : replace;
154     import std..string : indexOf;
155 
156     if (fullName)
157         str = str.replace(":", "_");
158 
159     else
160     {
161         auto i = str.indexOf(":");
162 
163         if (i > -1)
164             str = str[0 .. i];
165     }
166 
167     return translateIdentifier ? .translateIdentifier(str) : str;
168 }
169 
170 package string reduceAlias(Type type)
171 {
172     auto spelling = type.spelling;
173 
174     switch (type.spelling)
175     {
176         case "BOOL": return "bool";
177         case "int8_t": return "byte";
178         case "int16_t": return "short";
179         case "int32_t": return "int";
180         case "int64_t": return "long";
181         case "uint8_t": return "ubyte";
182         case "uint16_t": return "ushort";
183         case "uint32_t": return "uint";
184         case "uint64_t": return "ulong";
185 
186         case "__s8": return "byte";
187         case "__s16": return "short";
188         case "__s32": return "int";
189         case "__s64": return "long";
190         case "__u8": return "ubyte";
191         case "__u16": return "ushort";
192         case "__u32": return "uint";
193         case "__u64": return "ulong";
194 
195         case "s8": return "byte";
196         case "s16": return "short";
197         case "s32": return "int";
198         case "s64": return "long";
199         case "u8": return "ubyte";
200         case "u16": return "ushort";
201         case "u32": return "uint";
202         case "u64": return "ulong";
203 
204         default: return null;
205     }
206 }
207 
208 package bool isAliasReducible(Type type)
209 {
210     return reduceAlias(type) != null;
211 }
212 
213 private:
214 
215 string translateWCharT(Context context, Type type)
216 {
217     if (context.options.portableWCharT)
218     {
219         context.includeHandler.addImport("core.stdc.stddef");
220         return "wchar_t";
221     }
222     else if (type.canonical.kind.isIntegral)
223     {
224         auto sizeOf = type.canonical.sizeOf;
225 
226         if (sizeOf == 4)
227             return "dchar";
228         else if (sizeOf == 2)
229             return "wchar";
230     }
231 
232     return "<unimplemented>";
233 }
234 
235 string translateTypedef(Context context, Type type)
236 {
237     if (context.options.reduceAliases)
238     {
239         if (auto transl = reduceAlias(type))
240             return transl;
241     }
242 
243     auto spelling = type.spelling;
244 
245     with (CXTypeKind)
246         switch (spelling)
247         {
248             case "size_t":
249             case "ptrdiff_t":
250             case "sizediff_t":
251                 return spelling;
252 
253             case "wchar_t":
254                 return translateWCharT(context, type);
255 
256             default: break;
257         }
258 
259 
260     handleInclude(context, type);
261     return type.spelling;
262 }
263 
264 SourceNode translateUnexposed (Context context, Type type, bool rewriteIdToObjcObject)
265 in
266 {
267     assert(type.kind == CXTypeKind.unexposed);
268 }
269 body
270 {
271     auto declaration = type.declaration;
272 
273     if (declaration.isValid)
274         return translateType(context, declaration, rewriteIdToObjcObject);
275     else
276         return translateType(context, type.kind, rewriteIdToObjcObject)
277             .makeSourceNode();
278 }
279 
280 string translateComplex (Type type)
281 {
282     switch (type.element.kind)
283     {
284         case CXTypeKind.float_: return "cfloat";
285         case CXTypeKind.double_: return "cdouble";
286         case CXTypeKind.longDouble: return "creal";
287         default: return "<unimplemented>";
288     }
289 }
290 
291 SourceNode translateArrayElement(
292     Context context,
293     Cursor cursor,
294     ArrayType array,
295     bool rewriteIdToObjcObject)
296 {
297     import std.format : format;
298 
299     bool isConst = array.elementType.isConst;
300 
301     auto type = translateType(
302         context,
303         cursor,
304         array.elementType,
305         rewriteIdToObjcObject,
306         !isConst);
307 
308     if (isConst)
309         return type.wrapWith("const(", ")");
310     else
311         return type;
312 }
313 
314 SourceNode translateArray (
315     Context context,
316     Cursor cursor,
317     Type type,
318     bool rewriteIdToObjcObject,
319     size_t dimension = 0)
320 in
321 {
322     assert(type.kind == CXTypeKind.constantArray
323         || type.kind == CXTypeKind.incompleteArray);
324 }
325 body
326 {
327     import std.format : format;
328 
329     auto array = type.array;
330     SourceNode elementType;
331 
332     if (array.elementType.kind == CXTypeKind.constantArray)
333     {
334         elementType = translateArray(
335             context,
336             cursor,
337             array.elementType,
338             rewriteIdToObjcObject,
339             dimension == 0 ? 0 : dimension - 1);
340     }
341     else
342     {
343         elementType = translateArrayElement(
344             context,
345             cursor,
346             array,
347             rewriteIdToObjcObject);
348     }
349 
350     if (array.size >= 0)
351     {
352         auto children = cursor.filterChildren(
353             CXCursorKind.integerLiteral,
354             CXCursorKind.declRefExpr);
355 
356         auto maybeRef(T)(auto ref T value) {
357             return cursor.semanticParent.kind == CXCursorKind.functionDecl && dimension == 0
358                 ? elementType.wrapWith("ref ", format("[%s]", value))
359                 : elementType.suffixWith(format("[%s]", value));
360         }
361 
362         if (dimension < children.length)
363         {
364             if (children[dimension].kind == CXCursorKind.integerLiteral)
365             {
366                 auto token = tokenInsideSquareBrackets(cursor, dimension);
367 
368                 if (!token.isNull)
369                 {
370                     return maybeRef(token.spelling);
371                 }
372 
373                 auto expansions = context.macroIndex.queryExpansion(children[dimension]);
374 
375                 if (expansions.length == 1)
376                     return elementType.suffixWith(format("[%s]", expansions[0].spelling));
377             }
378             else if (children[dimension].kind == CXCursorKind.declRefExpr)
379             {
380                 return elementType.suffixWith(format("[%s]", children[dimension].spelling));
381             }
382         }
383 
384         return maybeRef(array.size);
385     }
386     else if (cursor.semanticParent.kind == CXCursorKind.functionDecl)
387     {
388         return elementType.suffixWith("*");
389     }
390     else
391     {
392         // FIXME: Find a way to translate references to static external arrays with unknown size.
393 
394         // extern static arrays (which are normally present in bindings)
395         // have same ABI as extern dynamic arrays, size is only checked
396         // against declaration in header. As it is not possible in D
397         // to define static array with ABI of dynamic one, only way is to
398         // abandon the size information
399         return elementType.suffixWith("[]");
400     }
401 }
402 
403 // find the token for a (possibly multidimensioned) array for a certain dimension,
404 // e.g. int foo[1][2][3] will find the "3" for dimension 0 due to the differences
405 // in array declarations between D and C
406 private Nullable!Token tokenInsideSquareBrackets(Cursor cursor, in size_t dimension)
407 {
408     import std.algorithm: find;
409     import std.range: retro;
410     import clang.Token: TokenKind;
411 
412     auto fromNextBracket(R)(R tokens)
413     {
414         return tokens.find!(a => a.kind == TokenKind.punctuation && a.spelling == "]");
415     }
416 
417     auto tokens = cursor.tokens.retro.find!(_ => true);
418 
419     // dimension + 1 since dimension is 0-indexed and we need to find at least one
420     foreach(_; 0 .. dimension + 1)
421     {
422         tokens = fromNextBracket(tokens);
423         if (tokens.empty) return typeof(return).init;
424         tokens.popFront;
425     }
426 
427     return tokens.empty
428         ? typeof(return).init
429         : typeof(return)(tokens.front);
430 }
431 
432 SourceNode translatePointer (
433     Context context,
434     Cursor cursor,
435     Type type,
436     bool rewriteIdToObjcObject,
437     bool applyConst)
438 in
439 {
440     assert(type.kind == CXTypeKind.pointer);
441 }
442 body
443 {
444     static bool valueTypeIsConst (Type type)
445     {
446         auto pointee = type.pointee;
447 
448         while (pointee.kind == CXTypeKind.pointer)
449             pointee = pointee.pointee;
450 
451         return pointee.isConst;
452     }
453 
454     auto result = translateType(context, cursor, type.pointee, rewriteIdToObjcObject, false);
455 
456     version (D1)
457     {
458         result = result ~ '*';
459     }
460     else
461     {
462         if (applyConst && valueTypeIsConst(type))
463         {
464             if (type.isConst)
465                 result = result.wrapWith("const ", "*");
466             else
467                 result = result.wrapWith("const(", ")*");
468         }
469         else
470             result = result.suffixWith("*");
471     }
472 
473     return result;
474 }
475 
476 Parameter translateParameter (Context context, Cursor parameter)
477 {
478     Parameter result;
479 
480     result.type = translateType(context, parameter);
481     result.name = parameter.spelling;
482     result.isConst = false;
483 
484     return result;
485 }
486 
487 Parameter[] translateParameters (Context context, Cursor cursor, FuncType func)
488 {
489     import std.array : Appender;
490 
491     auto result = Appender!(Parameter[])();
492     auto arguments = func.arguments;
493 
494     foreach (child; cursor.all)
495     {
496         if (child.kind == CXCursorKind.parmDecl)
497             result.put(translateParameter(context, child));
498     }
499 
500     return result.data;
501 }
502 
503 SourceNode translateFunctionPointerType (Context context, Cursor cursor, FuncType func)
504 {
505     auto params = translateParameters(context, cursor, func);
506     auto result = translateType(context, cursor, func.resultType);
507     auto spacer = context.options.spaceAfterFunctionName ? " " : "";
508     auto multiline = cursor.extent.isMultiline &&
509         !context.options.singleLineFunctionSignatures;
510 
511     return translateFunction(
512         result,
513         "function",
514         params,
515         func.isVariadic,
516         "",
517         spacer,
518         multiline);
519 }
520 
521 SourceNode translateObjCObjectPointerType (Context context, Cursor cursor, Type type)
522 in
523 {
524     assert(type.kind == CXTypeKind.objCObjectPointer && !type.isObjCBuiltinType);
525 }
526 body
527 {
528     auto pointee = type.pointee;
529 
530     if (pointee.spelling == "Protocol")
531         return "Protocol*".makeSourceNode();
532 
533     else
534         return translateType(context, cursor, pointee);
535 }
536 
537 string translateType (Context context, CXTypeKind kind, bool rewriteIdToObjcObject = true)
538 {
539     import std.conv;
540 
541     with (CXTypeKind)
542         switch (kind)
543         {
544             case invalid: return "<unimplemented>";
545             case unexposed: return "<unimplemented>";
546             case void_: return "void";
547             case bool_: return "bool";
548             case charU: return "<unimplemented>";
549             case uChar: return "ubyte";
550             case char16: return "wchar";
551             case char32: return "dchar";
552             case uShort: return "ushort";
553             case uInt: return "uint";
554 
555             case uLong:
556                 context.includeHandler.addCompatible();
557                 return "c_ulong";
558 
559             case uLongLong: return "ulong";
560             case uInt128: return "<unimplemented>";
561             case charS: return "char";
562             case sChar: return "byte";
563             case wChar: return "wchar";
564             case short_: return "short";
565             case int_: return "int";
566 
567             case long_:
568                 context.includeHandler.addCompatible();
569                 return "c_long";
570 
571             case longLong: return "long";
572             case int128: return "<unimplemented>";
573             case float_: return "float";
574             case double_: return "double";
575             case longDouble: return "real";
576             case nullPtr: return "null";
577             case overload: return "<unimplemented>";
578             case dependent: return "<unimplemented>";
579             case objCId: return rewriteIdToObjcObject ? "ObjcObject" : "id";
580             case objCClass: return "Class";
581             case objCSel: return "SEL";
582 
583             case pointer:
584             case blockPointer:
585             case lValueReference:
586             case rValueReference:
587             case record:
588             case enum_:
589             case typedef_:
590             case functionNoProto:
591             case functionProto:
592             case vector:
593             case incompleteArray:
594             case variableArray:
595             case dependentSizedArray:
596             case memberPointer:
597             case elaborated:
598                 return "<unimplemented>";
599 
600             default: assert(0, "Unhandled type kind " ~ to!string(kind));
601         }
602 }