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 }