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.mutate.backend.analyze.id_factory;
11 
12 import logger = std.experimental.logger;
13 
14 import dextool.type : Path;
15 
16 import dextool.plugin.mutate.backend.analyze.extensions;
17 import dextool.plugin.mutate.backend.analyze.internal;
18 
19 @safe:
20 
21 /** Create mutation ID's from source code mutations.
22  *
23  * Note that the checksum is only based on the content and how it is changed.
24  * The filename is not part of the checksum. This mean that the checksum will
25  * "detect and reattach" to a file after a rename.
26  *
27  * The algorithm is a checksum of:
28  *  * the content of all relevant tokens, e.g. all except comments
29  *  * the token position before and after the mutant.
30  *  * the original text
31  *  * the mutated text
32  */
33 struct MutationIdFactory {
34     import my.hash : Checksum128, BuildChecksum128, toBytes, toChecksum128;
35     import dextool.plugin.mutate.backend.type : CodeMutant, CodeChecksum, Mutation, Checksum;
36     import dextool.type : Path;
37 
38     private {
39         /// Checksum of the filename containing the mutants.
40         Checksum file;
41         /// Checksum of all tokens content.
42         Checksum content;
43 
44         /// Where in the token stream the preMutant calculation is.
45         size_t preIdx;
46         Checksum preMutant;
47         /// Where in the post tokens the postMutant is.
48         size_t postIdx;
49         Checksum postMutant;
50     }
51 
52     /**
53      * Params:
54      * filename = the file that the factory is for
55      * file = checksum of the filename.
56      * tokens = all tokens from the file.
57      */
58     this(Path fileName, Token[] tokens) {
59         file = () {
60             BuildChecksum128 bc;
61             bc.put(cast(const(ubyte)[]) fileName.toString);
62             return toChecksum128(bc);
63         }();
64 
65         content = () {
66             BuildChecksum128 bc;
67             foreach (t; tokens) {
68                 bc.put(cast(const(ubyte)[]) t.spelling);
69             }
70             return toChecksum128(bc);
71         }();
72     }
73 
74     /// Update the number of tokens that are before and after the mutant.
75     void updatePosition(const size_t preCnt, const size_t postCnt) {
76         // only do it if the position changes
77         if (preCnt == preIdx && postCnt == postIdx)
78             return;
79 
80         preIdx = preCnt;
81         postIdx = postCnt;
82 
83         {
84             BuildChecksum128 bc;
85             bc.put(preIdx.toBytes);
86             preMutant = toChecksum128(bc);
87         }
88         {
89             BuildChecksum128 bc;
90             bc.put(postIdx.toBytes);
91             postMutant = toChecksum128(bc);
92         }
93     }
94 
95     /// Calculate the unique ID for a specific mutation at this point.
96     Checksum128 makeId(const(ubyte)[] mut) @safe pure nothrow const @nogc scope {
97         BuildChecksum128 h;
98 
99         h.put(file.c0.toBytes);
100         h.put(file.c1.toBytes);
101 
102         h.put(content.c0.toBytes);
103         h.put(content.c1.toBytes);
104 
105         h.put(preMutant.c0.toBytes);
106         h.put(preMutant.c1.toBytes);
107 
108         h.put(mut);
109 
110         h.put(postMutant.c0.toBytes);
111         h.put(postMutant.c1.toBytes);
112         return toChecksum128(h);
113     }
114 
115     /// Create a mutant at this mutation point.
116     CodeMutant makeMutant(Mutation m, const(ubyte)[] mut) @safe pure nothrow const @nogc scope {
117         auto id = makeId(mut);
118         return CodeMutant(CodeChecksum(id), m);
119     }
120 }