1 /**
2 Date: 2015-2017, Joakim Brännström
3 License: MPL-2, Mozilla Public License 2.0
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 */
6 module dextool.plugin.backend.cpptestdouble.adapter;
7 
8 import std.typecons : Yes, No;
9 import logger = std.experimental.logger;
10 
11 import dsrcgen.cpp : CppModule;
12 
13 import dextool.type : MainNs, MainInterface;
14 
15 // TODO this is a mega include. Reduce it.
16 import cpptooling.data;
17 
18 import cpptooling.data : CppClass, CppNamespace, CppClassName, CppMethodName, USRType;
19 
20 // dfmt off
21 @safe:
22 private struct BuildAdapter {
23     BuildAdapter makeTestDouble(bool value) {
24         this.hasTestDouble = value;
25         return this;
26     }
27 
28     BuildAdapter makeInitGlobals(bool value) {
29         this.hasGlobalInitializer = value;
30         return this;
31     }
32 
33     CppClass finalize() {
34         import cpptooling.data : SimpleFmt, TypeId, PtrFmt;
35 
36         auto c = CppClass(className);
37 
38         CxParam[] params;
39 
40         if (hasTestDouble) {
41             auto attr = TypeAttr.init;
42             attr.isRef = Yes.isRef;
43             auto kind = TypeKind(TypeKind.PointerInfo(PtrFmt(TypeId(interfaceName)),
44                     USRType(interfaceName ~ "&"), [attr]));
45 
46             params ~= makeCxParam(TypeKindVariable(TypeKindAttr(kind,
47                     TypeAttr.init), CppVariable("inst")));
48         }
49 
50         if (hasGlobalInitializer) {
51             auto kind = TypeKind(TypeKind.SimpleInfo(SimpleFmt(TypeId(interfaceInitGlobal))));
52             params ~= makeCxParam(TypeKindVariable(TypeKindAttr(kind,
53                     TypeAttr.init), CppVariable("init_globals")));
54         }
55 
56         c.comment("Adapter connecting an interface with an implementation.");
57         c.comment("The lifetime of the connection is the same as the instance of the adapter.");
58 
59         c.put(CppCtor(makeUniqueUSR, classCtor, params, CppAccess(AccessType.Public)));
60         c.put(CppDtor(makeUniqueUSR, classDtor, CppAccess(AccessType.Public),
61                 CppVirtualMethod(MemberVirtualType.Normal)));
62 
63         return c;
64     }
65 
66 private:
67     CppClassName className;
68     CppClassName interfaceName;
69     CppClassName interfaceInitGlobal;
70     CppMethodName classCtor;
71     CppMethodName classDtor;
72     bool hasTestDouble;
73     bool hasGlobalInitializer;
74 }
75 // dfmt on
76 
77 /// Make a C++ adapter for an interface.
78 BuildAdapter makeAdapter(InterfaceT)(InterfaceT interface_name) {
79     return BuildAdapter(CppClassName("Adapter"), CppClassName(cast(string) interface_name),
80             CppClassName(cast(string) interface_name ~ "_InitGlobals"),
81             CppMethodName("Adapter"), CppMethodName("~Adapter"));
82 }
83 
84 /// make an anonymous namespace containing a ptr to an instance of a test
85 /// double that implement the interface needed.
86 CppNamespace makeSingleton(MainNs main_ns, MainInterface main_if) {
87     import cpptooling.data : CppVariable, CxGlobalVariable, makeUniqueUSR;
88     import cpptooling.data : TypeId, PtrFmt;
89 
90     auto attr = TypeAttr.init;
91     attr.isPtr = Yes.isPtr;
92     auto kind = TypeKind(TypeKind.PointerInfo(PtrFmt(TypeId(main_ns ~ "::" ~ main_if)),
93             USRType(main_ns ~ "::" ~ main_if ~ "*"), [attr]));
94 
95     auto v = CxGlobalVariable(makeUniqueUSR, TypeKindAttr(kind, TypeAttr.init),
96             CppVariable("test_double_inst"));
97     auto ns = CppNamespace.makeAnonymous();
98     ns.put(v);
99 
100     return ns;
101 }
102 
103 /** Generate an adapter implementation.
104  *
105  * The global is expected to be named test_double_inst.
106  */
107 void generateImpl(CppClass c, CppModule impl) {
108     import std.variant : visit;
109     import cpptooling.data;
110     import dsrcgen.c : E;
111 
112     // C'tor is expected to have N params.
113     // One of them must be named inst.
114     static void genCtor(const ref CppClass c, const ref CppCtor m, CppModule impl) {
115         // dfmt off
116         TypeKindVariable p0 = () @trusted {
117             import std.array;
118 
119             return m.paramRange().front.visit!(
120                 (TypeKindVariable tkv) => tkv,
121                 (TypeKindAttr tk) => TypeKindVariable(tk, CppVariable("inst")),
122                 (VariadicType vt) {
123                     logger.error("Variadic c'tor not supported:", m.toString);
124                     return TypeKindVariable(makeSimple("not supported"), CppVariable("not supported"));
125                 })();
126         }();
127         // dfmt on
128 
129         with (impl.ctor_body(m.name.get, E(p0.type.toStringDecl(p0.name)))) {
130             stmt(E("test_double_inst") = E("&" ~ p0.name));
131         }
132         impl.sep(2);
133     }
134 
135     static void genOp(const ref CppClass c, const ref CppMethodOp m, CppModule impl) {
136         // not applicable
137     }
138 
139     static void genDtor(const ref CppClass c, const ref CppDtor m, CppModule impl) {
140         with (impl.dtor_body(c.name)) {
141             stmt("test_double_inst = 0");
142         }
143         impl.sep(2);
144     }
145 
146     static void genMethod(const ref CppClass c, const ref CppMethod m, CppModule impl) {
147         import std.range : takeOne;
148 
149         string params = m.paramRange().joinParams();
150         auto b = impl.method_body(m.returnType.toStringDecl, c.name, m.name,
151                 m.isConst ? Yes.isConst : No.isConst, params);
152         with (b) {
153             auto p = m.paramRange().joinParamNames();
154             stmt(E("test_double_inst") = E("&" ~ p));
155         }
156         impl.sep(2);
157     }
158 
159     foreach (m; c.methodPublicRange()) {
160         // dfmt off
161         () @trusted{
162             m.visit!(
163                 (const CppMethod m) => genMethod(c, m, impl),
164                 (const CppMethodOp m) => genOp(c, m, impl),
165                 (const CppCtor m) => genCtor(c, m, impl),
166                 (const CppDtor m) => genDtor(c, m, impl));
167         }();
168         // dfmt on
169     }
170 }
171 
172 /// A singleton to allow the adapter to setup "a" connection.
173 void generateSingleton(CppNamespace in_ns, CppModule impl) {
174     import std.ascii : newline;
175     import cpptooling.data;
176     import dsrcgen.cpp : E;
177 
178     auto ns = impl.namespace("")[$.begin = "{" ~ newline];
179     ns.suppressIndent(1);
180     impl.sep(2);
181 
182     foreach (g; in_ns.globalRange()) {
183         auto stmt = E(g.type.toStringDecl(g.name));
184         if (g.type.kind.info.kind == TypeKind.Info.Kind.pointer) {
185             stmt = E("0");
186         }
187         ns.stmt(stmt);
188     }
189 }