// 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 Numbers mutator. */ 'use strict'; const babelTypes = require('@babel/types'); const common = require('./common.js'); const random = require('../random.js'); const mutator = require('./mutator.js'); const MIN_SAFE_INTEGER = -9007199254740991; const MAX_SAFE_INTEGER = 9007199254740991; function isObjectKey(path) { return (path.parent && babelTypes.isObjectMember(path.parent) && path.parent.key === path.node); } function createRandomNumber(value) { // TODO(ochang): Maybe replace with variable. const probability = random.random(); if (probability < 0.01) { return babelTypes.numericLiteral( random.randInt(MIN_SAFE_INTEGER, MAX_SAFE_INTEGER)); } else if (probability < 0.06) { return common.randomInterestingNumber(); } else { return common.nearbyRandomNumber(value); } } class NumberMutator extends mutator.Mutator { constructor(settings) { super(); this.settings = settings; } ignore(path) { return !random.choose(this.settings.MUTATE_NUMBERS) || common.isInForLoopCondition(path) || common.isInWhileLoop(path); } randomReplace(path, value, forcePositive=false) { const randomNumber = createRandomNumber(value); if (forcePositive) { randomNumber.value = Math.abs(randomNumber.value); } this.annotate( path.node, `Replaced ${value} with ${randomNumber.value}`); this.replaceWithSkip(path, randomNumber); } get visitor() { const thisMutator = this; return { NumericLiteral(path) { if (thisMutator.ignore(path)) { return; } // We handle negative unary expressions separately to replace the whole // expression below. E.g. -5 is UnaryExpression(-, NumericLiteral(5)). if (path.parent && babelTypes.isUnaryExpression(path.parent) && path.parent.operator === '-') { return; } // Enfore positive numbers if the literal is the key of an object // property or method. Negative keys cause syntax errors. const forcePositive = isObjectKey(path); thisMutator.randomReplace(path, path.node.value, forcePositive); }, UnaryExpression(path) { if (thisMutator.ignore(path)) { return; } // Handle the case we ignore above. if (path.node.operator === '-' && babelTypes.isNumericLiteral(path.node.argument)) { thisMutator.randomReplace(path, -path.node.argument.value); } } }; } } module.exports = { NumberMutator: NumberMutator, };