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     /// An instance is related to a filename.
39     Path fileName;
40 
41     /// Checksum of the filename containing the mutants.
42     Checksum file;
43     /// Checksum of all tokens content.
44     Checksum content;
45 
46     private {
47         /// Where in the token stream the preMutant calculation is.
48         size_t preIdx;
49         Checksum preMutant;
50         /// Where in the post tokens the postMutant is.
51         size_t postIdx;
52         Checksum postMutant;
53     }
54 
55     /**
56      * Params:
57      * filename = the file that the factory is for
58      * file = checksum of the filename.
59      * tokens = all tokens from the file.
60      */
61     this(Path fileName, Checksum file, Token[] tokens) {
62         this.fileName = fileName;
63         this.file = file;
64 
65         BuildChecksum128 bc;
66         foreach (t; tokens) {
67             bc.put(cast(const(ubyte)[]) t.spelling);
68         }
69         this.content = toChecksum128(bc);
70     }
71 
72     /// Update the number of tokens that are before and after the mutant.
73     void updatePosition(const size_t preCnt, const size_t postCnt) {
74         // only do it if the position changes
75         if (preCnt == preIdx && postCnt == postIdx)
76             return;
77 
78         preIdx = preCnt;
79         postIdx = postCnt;
80 
81         {
82             BuildChecksum128 bc;
83             bc.put(preIdx.toBytes);
84             preMutant = toChecksum128(bc);
85         }
86         {
87             BuildChecksum128 bc;
88             bc.put(postIdx.toBytes);
89             postMutant = toChecksum128(bc);
90         }
91     }
92 
93     /// Calculate the unique ID for a specific mutation at this point.
94     Checksum128 makeId(const(ubyte)[] mut) @safe pure nothrow const @nogc scope {
95         // # SPC-analyzer-checksum
96         BuildChecksum128 h;
97         h.put(file.c0.toBytes);
98         h.put(file.c1.toBytes);
99 
100         h.put(content.c0.toBytes);
101         h.put(content.c1.toBytes);
102 
103         h.put(preMutant.c0.toBytes);
104         h.put(preMutant.c1.toBytes);
105 
106         h.put(mut);
107 
108         h.put(postMutant.c0.toBytes);
109         h.put(postMutant.c1.toBytes);
110         return toChecksum128(h);
111     }
112 
113     /// Create a mutant at this mutation point.
114     CodeMutant makeMutant(Mutation m, const(ubyte)[] mut) @safe pure nothrow const @nogc scope {
115         auto id = makeId(mut);
116         return CodeMutant(CodeChecksum(id), m);
117     }
118 }