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 }