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 my.sumtype;
13 
14 import cpptooling.data.representation : CppClass;
15 import cpptooling.data.symbol : Container;
16 import cpptooling.data.symbol.types : FullyQualifiedNameType;
17 
18 import std.typecons : NullableRef;
19 import std.range : only;
20 
21 @safe:
22 
23 alias LookupT = typeof(only(NullableRef!(CppClass).init)) delegate(FullyQualifiedNameType a);
24 
25 /** Create a merged class with methods from all those classes it inherits from.
26  *
27  * Params:
28  *  LookupT = a callback that takes a FullyQualifiedNameType and returns a
29  *      range with zero or one elements of type NullableRef!CppClass.
30  */
31 CppClass mergeClassInherit(ref CppClass class_, ref Container container, LookupT lookup) {
32     import std.algorithm : each;
33 
34     if (class_.inheritRange.length == 0) {
35         return class_;
36     }
37 
38     auto methods = dedup(getMethods(class_, container, lookup));
39 
40     auto c = CppClass(class_.name, class_.inherits, class_.resideInNs);
41     () @trusted { methods.each!(a => c.put(a)); }();
42 
43     return c;
44 }
45 
46 private:
47 
48 bool isMethodOrOperator(T)(T method) @trusted {
49     import cpptooling.data.representation : CppMethod, CppMethodOp, CppCtor, CppDtor;
50 
51     // dfmt off
52     return method.match!((CppMethod a) => true,
53                          (CppMethodOp a) => true,
54                          (CppCtor a) => false,
55                          (CppDtor a) => false);
56     // dfmt on
57 }
58 
59 CppClass.CppFunc[] getMethods(ref CppClass c, 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 cpptooling.data : CppMethod, CppMethodOp, CppCtor, CppDtor;
90 
91         // dfmt off
92         return method.match!((CppMethod a) => a.id,
93                              (CppMethodOp a) => a.id,
94                              (CppCtor a) => a.id,
95                              (CppDtor a) => a.id);
96         // dfmt on
97     }
98 
99     //TODO inefficient, lots of intermittent arrays and allocations.
100     // Convert to a range based no-allocation.
101 
102     auto arr = methods.map!(a => getUniqeId(a)).array();
103 
104     auto index = new size_t[arr.length];
105     // sorting the indexes
106     makeIndex(arr, index);
107 
108     // dfmt off
109     // contains a list of indexes into methods
110     auto deduped_methods =
111         index
112         // dedup the sorted index
113         .uniq!((a,b) => arr[a] == arr[b])
114         .array();
115 
116     // deterministic sorting by function signature
117     deduped_methods.sort!((a,b) { return methods[a].funcToString < methods[b].funcToString; });
118 
119     return deduped_methods
120         // reconstruct an array from the sorted indexes
121         .map!(a => methods[a])
122         .array();
123     // dfmt on
124 }