1// Copyright 2020 the V8 project authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5/** 6 * @fileoverview Variables mutator. 7 */ 8 9'use strict'; 10 11const babelTemplate = require('@babel/template').default; 12const babelTypes = require('@babel/types'); 13 14const common = require('./common.js'); 15const random = require('../random.js'); 16const mutator = require('./mutator.js'); 17 18const MAX_MUTATION_RECURSION_DEPTH = 5; 19 20class VariableOrObjectMutator extends mutator.Mutator { 21 constructor(settings) { 22 super(); 23 this.settings = settings; 24 } 25 26 _randomVariableOrObject(path) { 27 const randomVar = common.randomVariable(path); 28 if (random.choose(0.05) || !randomVar) { 29 return common.randomObject(); 30 } 31 32 return randomVar; 33 } 34 35 _randomVariableOrObjectMutations(path, recurseDepth=0) { 36 if (recurseDepth >= MAX_MUTATION_RECURSION_DEPTH) { 37 return new Array(); 38 } 39 40 const probability = random.random(); 41 42 if (probability < 0.3) { 43 const first = this._randomVariableOrObjectMutations(path, recurseDepth + 1); 44 const second = this._randomVariableOrObjectMutations( 45 path, recurseDepth + 1); 46 return first.concat(second); 47 } 48 49 const randVarOrObject = this._randomVariableOrObject(path); 50 const randProperty = common.randomProperty(randVarOrObject); 51 let newRandVarOrObject = randVarOrObject; 52 if (random.choose(0.2)) { 53 newRandVarOrObject = this._randomVariableOrObject(path); 54 } 55 56 const mutations = new Array(); 57 58 if (probability < 0.4) { 59 const template = babelTemplate( 60 'delete IDENTIFIER[PROPERTY], __callGC()') 61 mutations.push(template({ 62 IDENTIFIER: randVarOrObject, 63 PROPERTY: randProperty 64 })); 65 } else if (probability < 0.5) { 66 const template = babelTemplate( 67 'IDENTIFIER[PROPERTY], __callGC()') 68 mutations.push(template({ 69 IDENTIFIER: randVarOrObject, 70 PROPERTY: randProperty 71 })); 72 } else if (probability < 0.6) { 73 const template = babelTemplate( 74 'IDENTIFIER[PROPERTY] = RANDOM, __callGC()') 75 mutations.push(template({ 76 IDENTIFIER: randVarOrObject, 77 PROPERTY: randProperty, 78 RANDOM: common.randomValue(path), 79 })); 80 } else if (probability < 0.7) { 81 mutations.push( 82 babelTypes.expressionStatement( 83 common.callRandomFunction(path, randVarOrObject))); 84 } else if (probability < 0.8) { 85 const template = babelTemplate( 86 'VAR = IDENTIFIER, __callGC()') 87 var randomVar = common.randomVariable(path); 88 if (!randomVar) { 89 return mutations; 90 } 91 92 mutations.push(template({ 93 VAR: randomVar, 94 IDENTIFIER: randVarOrObject, 95 })); 96 } else if (probability < 0.9) { 97 const template = babelTemplate( 98 'if (IDENTIFIER != null && typeof(IDENTIFIER) == "object") ' + 99 'Object.defineProperty(IDENTIFIER, PROPERTY, {value: VALUE})') 100 mutations.push(template({ 101 IDENTIFIER: newRandVarOrObject, 102 PROPERTY: randProperty, 103 VALUE: common.randomValue(path), 104 })); 105 } else { 106 const template = babelTemplate( 107 'if (IDENTIFIER != null && typeof(IDENTIFIER) == "object") ' + 108 'Object.defineProperty(IDENTIFIER, PROPERTY, {' + 109 'get: function() { GETTER_MUTATION ; return VALUE; },' + 110 'set: function(value) { SETTER_MUTATION; }' + 111 '})'); 112 mutations.push(template({ 113 IDENTIFIER: newRandVarOrObject, 114 PROPERTY: randProperty, 115 GETTER_MUTATION: this._randomVariableOrObjectMutations( 116 path, recurseDepth + 1), 117 SETTER_MUTATION: this._randomVariableOrObjectMutations( 118 path, recurseDepth + 1), 119 VALUE: common.randomValue(path), 120 })); 121 } 122 123 return mutations; 124 } 125 126 127 get visitor() { 128 const settings = this.settings; 129 const thisMutator = this; 130 131 return { 132 ExpressionStatement(path) { 133 if (!random.choose(settings.ADD_VAR_OR_OBJ_MUTATIONS)) { 134 return; 135 } 136 137 const mutations = thisMutator._randomVariableOrObjectMutations(path); 138 thisMutator.annotate(mutations[0], 'Random mutation'); 139 140 if (random.choose(0.5)) { 141 thisMutator.insertBeforeSkip(path, mutations); 142 } else { 143 thisMutator.insertAfterSkip(path, mutations); 144 } 145 146 path.skip(); 147 } 148 }; 149 } 150} 151 152module.exports = { 153 VariableOrObjectMutator: VariableOrObjectMutator, 154}; 155