// 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 Expression mutator.
 */

'use strict';

const babelTypes = require('@babel/types');

const random = require('../random.js');
const mutator = require('./mutator.js');

class ExpressionMutator extends mutator.Mutator {
  get visitor() {
    const thisMutator = this;

    return {
      ExpressionStatement(path) {
        if (!random.choose(thisMutator.settings.MUTATE_EXPRESSIONS)) {
          return;
        }

        const probability = random.random();

        if (probability < 0.7) {
          const repeated = babelTypes.cloneDeep(path.node);
          thisMutator.annotate(repeated, 'Repeated');
          thisMutator.insertBeforeSkip(path, repeated);
        } else if (path.key > 0) {
          // Get a random previous sibling.
          const prev = path.getSibling(random.randInt(0, path.key - 1));
          if (!prev || !prev.node) {
            return;
          }
          // Either select a previous or the current node to clone. Bias
          // towards the former, as the other way can lead to use of undefined
          // variables.
          const [selected, destination] = random.single(
              [[prev, path], [prev, path], [prev, path], [path, prev]]);
          if (selected.isDeclaration()) {
            return;
          }
          const cloned = babelTypes.cloneDeep(selected.node);
          thisMutator.annotate(cloned, 'Cloned sibling');
          if (random.choose(0.5)) {
            thisMutator.insertBeforeSkip(destination, cloned);
          } else {
            thisMutator.insertAfterSkip(destination, cloned);
          }
        }
      },
    };
  }
}

module.exports = {
  ExpressionMutator: ExpressionMutator,
};
