blob: 4f34e15c06e67c86d17ef2d9d084e99b9e430526 [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 Function calls 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');
function _liftExpressionsToStatements(path, nodes) {
// If the node we're replacing is an expression in an expression statement,
// lift the replacement nodes into statements too.
if (!babelTypes.isExpressionStatement(path.parent)) {
return nodes;
}
return nodes.map(n => babelTypes.expressionStatement(n));
}
class FunctionCallMutator extends mutator.Mutator {
constructor(settings) {
super();
this.settings = settings;
}
get visitor() {
const thisMutator = this;
return {
CallExpression(path) {
if (!babelTypes.isIdentifier(path.node.callee)) {
return;
}
if (!common.isFunctionIdentifier(path.node.callee.name)) {
return;
}
if (!random.choose(thisMutator.settings.MUTATE_FUNCTION_CALLS)) {
return;
}
const probability = random.random();
if (probability < 0.5) {
const randFunc = common.randomFunction(path);
if (randFunc) {
thisMutator.annotate(
path.node,
`Replaced ${path.node.callee.name} with ${randFunc.name}`);
path.node.callee = randFunc;
}
} else if (probability < 0.7 && thisMutator.settings.engine == 'V8') {
const prepareTemplate = babelTemplate(
'__V8BuiltinPrepareFunctionForOptimization(ID)');
const optimizeTemplate = babelTemplate(
'__V8BuiltinOptimizeFunctionOnNextCall(ID)');
const nodes = [
prepareTemplate({
ID: babelTypes.cloneDeep(path.node.callee),
}).expression,
babelTypes.cloneDeep(path.node),
babelTypes.cloneDeep(path.node),
optimizeTemplate({
ID: babelTypes.cloneDeep(path.node.callee),
}).expression,
];
thisMutator.annotate(
path.node,
`Optimizing ${path.node.callee.name}`);
if (!babelTypes.isExpressionStatement(path.parent)) {
nodes.push(path.node);
thisMutator.replaceWithSkip(
path, babelTypes.sequenceExpression(nodes));
} else {
thisMutator.insertBeforeSkip(
path, _liftExpressionsToStatements(path, nodes));
}
} else if (probability < 0.85 &&
thisMutator.settings.engine == 'V8') {
const template = babelTemplate(
'__V8BuiltinDeoptimizeFunction(ID)');
const insert = _liftExpressionsToStatements(path, [
template({
ID: babelTypes.cloneDeep(path.node.callee),
}).expression,
]);
thisMutator.annotate(
path.node,
`Deoptimizing ${path.node.callee.name}`);
thisMutator.insertAfterSkip(path, insert);
} else {
const template = babelTemplate(
'runNearStackLimit(() => { return CALL });');
thisMutator.annotate(
path.node,
`Run to stack limit ${path.node.callee.name}`);
thisMutator.replaceWithSkip(
path,
template({
CALL: path.node,
}).expression);
}
path.skip();
},
}
}
}
module.exports = {
FunctionCallMutator: FunctionCallMutator,
};