blob: 3c96464c818747393ebbf5215c17dd89c84357a0 [file] [log] [blame]
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
/**
* @fileoverview Variables mutator.
*/
'use strict';
const babelTemplate = require('@babel/template').default;
const babelTypes = require('@babel/types');
const common = require('./common.js');
const random = require('../random.js');
const mutator = require('./mutator.js');
const MAX_MUTATION_RECURSION_DEPTH = 5;
class VariableOrObjectMutator extends mutator.Mutator {
constructor(settings) {
super();
this.settings = settings;
}
_randomVariableOrObject(path) {
const randomVar = common.randomVariable(path);
if (random.choose(0.05) || !randomVar) {
return common.randomObject();
}
return randomVar;
}
_randomVariableOrObjectMutations(path, recurseDepth=0) {
if (recurseDepth >= MAX_MUTATION_RECURSION_DEPTH) {
return new Array();
}
const probability = random.random();
if (probability < 0.3) {
const first = this._randomVariableOrObjectMutations(path, recurseDepth + 1);
const second = this._randomVariableOrObjectMutations(
path, recurseDepth + 1);
return first.concat(second);
}
const randVarOrObject = this._randomVariableOrObject(path);
const randProperty = common.randomProperty(randVarOrObject);
let newRandVarOrObject = randVarOrObject;
if (random.choose(0.2)) {
newRandVarOrObject = this._randomVariableOrObject(path);
}
const mutations = new Array();
if (probability < 0.4) {
const template = babelTemplate(
'delete IDENTIFIER[PROPERTY], __callGC()')
mutations.push(template({
IDENTIFIER: randVarOrObject,
PROPERTY: randProperty
}));
} else if (probability < 0.5) {
const template = babelTemplate(
'IDENTIFIER[PROPERTY], __callGC()')
mutations.push(template({
IDENTIFIER: randVarOrObject,
PROPERTY: randProperty
}));
} else if (probability < 0.6) {
const template = babelTemplate(
'IDENTIFIER[PROPERTY] = RANDOM, __callGC()')
mutations.push(template({
IDENTIFIER: randVarOrObject,
PROPERTY: randProperty,
RANDOM: common.randomValue(path),
}));
} else if (probability < 0.7) {
mutations.push(
babelTypes.expressionStatement(
common.callRandomFunction(path, randVarOrObject)));
} else if (probability < 0.8) {
const template = babelTemplate(
'VAR = IDENTIFIER, __callGC()')
var randomVar = common.randomVariable(path);
if (!randomVar) {
return mutations;
}
mutations.push(template({
VAR: randomVar,
IDENTIFIER: randVarOrObject,
}));
} else if (probability < 0.9) {
const template = babelTemplate(
'if (IDENTIFIER != null && typeof(IDENTIFIER) == "object") ' +
'Object.defineProperty(IDENTIFIER, PROPERTY, {value: VALUE})')
mutations.push(template({
IDENTIFIER: newRandVarOrObject,
PROPERTY: randProperty,
VALUE: common.randomValue(path),
}));
} else {
const template = babelTemplate(
'if (IDENTIFIER != null && typeof(IDENTIFIER) == "object") ' +
'Object.defineProperty(IDENTIFIER, PROPERTY, {' +
'get: function() { GETTER_MUTATION ; return VALUE; },' +
'set: function(value) { SETTER_MUTATION; }' +
'})');
mutations.push(template({
IDENTIFIER: newRandVarOrObject,
PROPERTY: randProperty,
GETTER_MUTATION: this._randomVariableOrObjectMutations(
path, recurseDepth + 1),
SETTER_MUTATION: this._randomVariableOrObjectMutations(
path, recurseDepth + 1),
VALUE: common.randomValue(path),
}));
}
return mutations;
}
get visitor() {
const settings = this.settings;
const thisMutator = this;
return {
ExpressionStatement(path) {
if (!random.choose(settings.ADD_VAR_OR_OBJ_MUTATIONS)) {
return;
}
const mutations = thisMutator._randomVariableOrObjectMutations(path);
thisMutator.annotate(mutations[0], 'Random mutation');
if (random.choose(0.5)) {
thisMutator.insertBeforeSkip(path, mutations);
} else {
thisMutator.insertAfterSkip(path, mutations);
}
path.skip();
}
};
}
}
module.exports = {
VariableOrObjectMutator: VariableOrObjectMutator,
};