1 /**
2 Copyright: Copyright (c) 2017, Joakim Brännström. All rights reserved.
3 License: MPL-2
4 Author: Joakim Brännström (joakim.brannstrom@gmx.com)
5 
6 This Source Code Form is subject to the terms of the Mozilla Public License,
7 v.2.0. If a copy of the MPL was not distributed with this file, You can obtain
8 one at http://mozilla.org/MPL/2.0/.
9 */
10 module cpptooling.data.class_merge;
11 
12 import cpptooling.data.representation : CppClass;
13 import cpptooling.data.symbol : Container;
14 import cpptooling.data.symbol.types : FullyQualifiedNameType;
15 
16 import std.typecons : NullableRef;
17 import std.range : only;
18 
19 @safe:
20 
21 alias LookupT = typeof(only(NullableRef!(CppClass).init)) delegate(FullyQualifiedNameType a);
22 
23 /** Create a merged class with methods from all those classes it inherits from.
24  *
25  * Params:
26  *  LookupT = a callback that takes a FullyQualifiedNameType and returns a
27  *      range with zero or one elements of type NullableRef!CppClass.
28  */
29 CppClass mergeClassInherit(ref CppClass class_, const ref Container container, LookupT lookup) {
30     import std.algorithm : each;
31 
32     if (class_.inheritRange.length == 0) {
33         return class_;
34     }
35 
36     auto methods = dedup(getMethods(class_, container, lookup));
37 
38     auto c = CppClass(class_.name, class_.inherits, class_.resideInNs);
39     () @trusted{ methods.each!(a => c.put(a)); }();
40 
41     return c;
42 }
43 
44 private:
45 
46 bool isMethodOrOperator(T)(T method) @trusted {
47     import std.variant : visit;
48     import cpptooling.data.representation : CppMethod, CppMethodOp, CppCtor,
49         CppDtor;
50 
51     // dfmt off
52     return method.visit!((const CppMethod a) => true,
53                          (const CppMethodOp a) => true,
54                          (const CppCtor a) => false,
55                          (const CppDtor a) => false);
56     // dfmt on
57 }
58 
59 CppClass.CppFunc[] getMethods(const ref CppClass c, const ref Container container, LookupT lookup) @safe {
60     import std.array : array, appender;
61     import std.algorithm : cache, copy, each, filter, joiner, map;
62     import std.range : chain;
63 
64     // dfmt off
65     auto local_methods = c.methodRange
66         .filter!(a => isMethodOrOperator(a));
67 
68     auto inherit_methods = c.inheritRange
69         .map!(a => lookup(a.fullyQualifiedName))
70         // some classes do not exist in AST thus no methods returned
71         .joiner
72         .map!(a => getMethods(a, container, lookup));
73     // dfmt on
74 
75     auto methods = appender!(CppClass.CppFunc[])();
76     () @trusted{ local_methods.copy(methods); inherit_methods.copy(methods); }();
77 
78     return methods.data;
79 }
80 
81 //TODO this function is inefficient. So many allocations...
82 auto dedup(CppClass.CppFunc[] methods) @trusted {
83     import std.array : array;
84     import std.algorithm : makeIndex, uniq, map, sort;
85     import cpptooling.utility.dedup : dedup;
86     import cpptooling.data.representation : funcToString;
87 
88     static auto getUniqeId(T)(ref T method) {
89         import std.variant : visit;
90         import cpptooling.data : CppMethod, CppMethodOp, CppCtor, CppDtor;
91 
92         // dfmt off
93         return method.visit!((CppMethod a) => a.id,
94                              (CppMethodOp a) => a.id,
95                              (CppCtor a) => a.id,
96                              (CppDtor a) => a.id);
97         // dfmt on
98     }
99 
100     //TODO inefficient, lots of intermittent arrays and allocations.
101     // Convert to a range based no-allocation.
102 
103     auto arr = methods.map!(a => getUniqeId(a)).array();
104 
105     auto index = new size_t[arr.length];
106     // sorting the indexes
107     makeIndex(arr, index);
108 
109     // dfmt off
110     // contains a list of indexes into methods
111     auto deduped_methods =
112         index
113         // dedup the sorted index
114         .uniq!((a,b) => arr[a] == arr[b])
115         .array();
116 
117     // deterministic sorting by function signature
118     deduped_methods.sort!((a,b) { return methods[a].funcToString < methods[b].funcToString; });
119 
120     return deduped_methods
121         // reconstruct an array from the sorted indexes
122         .map!(a => methods[a])
123         .array();
124     // dfmt on
125 }