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 dextool.plugin.fuzzer.backend.generate_xml;
11 
12 import std.conv : to;
13 
14 import logger = std.experimental.logger;
15 
16 import cpptooling.data : CppRoot, CFunction, USRType, CxParam, CppVariable;
17 
18 import dextool.compilation_db : CompileCommandFilter;
19 
20 import dextool.plugin.fuzzer.type : Param, Symbol;
21 import dextool.plugin.fuzzer.backend.type;
22 import dextool.plugin.fuzzer.backend.unique_sequence : Sequence;
23 
24 /// TODO change to @safe when the base compiler is upgraded to 2.074+
25 void generateConfigCompilerFilter(ref CompileCommandFilter compiler_flag_filter,
26         ref TemplateConfig tmpl_conf) @trusted {
27     import std.conv : to;
28     import std.xml;
29 
30     auto compiler_tag = new Element("compiler_flag_filter");
31     compiler_tag.tag.attr["skip_compiler_args"] = compiler_flag_filter
32         .skipCompilerArgs.to!string();
33     foreach (value; compiler_flag_filter.filter) {
34         auto tag = new Element("exclude");
35         tag ~= new Text(value);
36         compiler_tag ~= tag;
37     }
38 
39     tmpl_conf.doc ~= compiler_tag;
40 }
41 
42 /// TODO change to @safe when the base compiler is upgraded to 2.074+
43 void generateConfigPerFunction(ref ImplData impl, ref TemplateConfig tmpl_conf) @trusted {
44     import std.xml;
45     import cpptooling.data.symbol.types : FullyQualifiedNameType;
46 
47     // no duplications are allowed
48     bool[FullyQualifiedNameType] generated_syms;
49 
50     // dump the user excluded symbols
51     foreach (s; impl.excludedSymbols) {
52         auto sym = tmpl_conf.makeSymbol;
53         sym.tag.attr["name"] = cast(FullyQualifiedNameType) s;
54         sym.tag.attr["filter"] = "exclude";
55         generated_syms[s] = true;
56     }
57 
58     foreach (f; impl.root.funcRange) {
59         if (auto s = FullyQualifiedNameType(f.name) in generated_syms) {
60             continue;
61         }
62 
63         auto sym = tmpl_conf.makeSymbol;
64 
65         if (auto s = FullyQualifiedNameType(f.name) in impl.symbols) {
66             generateConfigForFunctionValidityFromUser(f, *s, sym);
67         } else {
68             generateConfigDefaultFunctionValidity(f,
69                     impl.symbolId[FullyQualifiedNameType(f.name)], sym);
70         }
71     }
72 }
73 
74 void generateConfigForFunctionValidityFromUser(T)(CFunction f, Symbol symbol, ref T sym) {
75     import std.algorithm : map;
76     import std.array : array;
77     import std.conv : to;
78     import std.range : enumerate;
79     import std.xml;
80     import cpptooling.data : getName;
81 
82     // assuming that the fully qualified name is always valid xml
83     sym.tag.attr["name"] = symbol.fullyQualifiedName;
84     sym.tag.attr["id"] = symbol.sequenceId.to!string;
85 
86     string[] param_ids = f.paramRange.enumerate.map!(
87             a => a.value.getName("param" ~ a.index.to!string)).array();
88 
89     if (symbol.hasFuzz) {
90         auto fuzz = new Element("fuzz");
91         sym ~= fuzz;
92         fuzz.tag.attr["use"] = symbol.fuzz.use;
93         fuzz.tag.attr["include"] = symbol.fuzz.include;
94     }
95 
96     foreach (idx, p; symbol.limits.enumerate) {
97         //TODO this sanity check should be at the translation stage. Not the code generation.
98         if (idx >= param_ids.length) {
99             logger.warningf("symbol '%s', too many parameter checkers. Discarding '%s'",
100                     symbol.fullyQualifiedName, p.identifier);
101             continue;
102         } else if (param_ids[idx] != p.identifier) {
103             logger.infof("symbol '%s', parameter identifier '%s' do not match the functions parameter '%s'",
104                     f.name, p.identifier, param_ids[idx]);
105         }
106 
107         auto xparam = new Element("param");
108         sym ~= xparam;
109 
110         xparam.tag.attr["name"] = p.identifier;
111 
112         if (p.hasCheck) {
113             auto valid = new Element("valid");
114             xparam ~= valid;
115             valid.tag.attr["check"] = p.check;
116             valid.tag.attr["condition"] = p.condition;
117         }
118 
119         if (p.hasFuzz) {
120             auto fuzz = new Element("fuzz");
121             xparam ~= fuzz;
122             fuzz.tag.attr["use"] = p.fuzz.use;
123             fuzz.tag.attr["param"] = p.fuzz.param;
124             fuzz.tag.attr["include"] = p.fuzz.include;
125         }
126     }
127 }
128 
129 void generateConfigDefaultFunctionValidity(T)(CFunction f, ulong seq_id, ref T sym) {
130     import std.conv : to;
131     import std.range : enumerate;
132     import std.xml;
133     import cpptooling.data : getName;
134 
135     // assuming that the fully qualified name is always valid xml
136     sym.tag.attr["name"] = f.name;
137     sym.tag.attr["id"] = seq_id.to!string;
138 
139     foreach (idx, p; f.paramRange.enumerate) {
140         auto xparam = new Element("param");
141         sym ~= xparam;
142 
143         xparam.tag.attr["name"] = p.getName("param" ~ idx.to!string);
144 
145         auto valid = new Element("valid");
146         xparam ~= valid;
147         valid.tag.attr["check"] = "";
148         valid.tag.attr["condition"] = "";
149 
150         auto fuzz = new Element("fuzz");
151         xparam ~= fuzz;
152         fuzz.tag.attr["use"] = "";
153         fuzz.tag.attr["param"] = "";
154         fuzz.tag.attr["include"] = "";
155     }
156 }