• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 Mutator for array expressions.
7 */
8
9'use strict';
10
11const babelTypes = require('@babel/types');
12
13const common = require('./common.js');
14const mutator = require('./mutator.js');
15const random = require('../random.js');
16
17// Blueprint for choosing the maximum number of mutations. Bias towards
18// performing only one mutation.
19const MUTATION_CHOICES = [1, 1, 1, 1, 1, 2, 2, 2, 3];
20
21const MAX_ARRAY_LENGTH = 50;
22
23class ArrayMutator extends mutator.Mutator {
24  constructor(settings) {
25    super();
26    this.settings = settings;
27  }
28
29  get visitor() {
30    const thisMutator = this;
31
32    return {
33      ArrayExpression(path) {
34        const elements = path.node.elements;
35        if (!random.choose(thisMutator.settings.MUTATE_ARRAYS) ||
36            elements.length > MAX_ARRAY_LENGTH) {
37          return;
38        }
39
40        // Annotate array expression with the action taken, indicating
41        // if we also replaced elements.
42        function annotate(message, replace) {
43          if (replace) message += ' (replaced)';
44          thisMutator.annotate(path.node, message);
45        }
46
47        // Add or replace elements at a random index.
48        function randomSplice(replace, ...args) {
49          // Choose an index that's small enough to replace all desired items.
50          const index = random.randInt(0, elements.length - replace);
51          elements.splice(index, replace, ...args);
52        }
53
54        function duplicateElement(replace) {
55          const element = random.single(elements);
56          if (!element || common.isLargeNode(element)) {
57            return;
58          }
59          annotate('Duplicate an element', replace);
60          randomSplice(replace, babelTypes.cloneDeep(element));
61        }
62
63        function insertRandomValue(replace) {
64          annotate('Insert a random value', replace);
65          randomSplice(replace, common.randomValue(path));
66        }
67
68        function insertHole(replace) {
69          annotate('Insert a hole', replace);
70          randomSplice(replace, null);
71        }
72
73        function removeElements(count) {
74          annotate('Remove elements');
75          randomSplice(random.randInt(1, count));
76        }
77
78        function shuffle() {
79          annotate('Shuffle array');
80          random.shuffle(elements);
81        }
82
83        // Mutation options. Repeated mutations have a higher probability.
84        const mutations = [
85          () => duplicateElement(1),
86          () => duplicateElement(1),
87          () => duplicateElement(1),
88          () => duplicateElement(0),
89          () => duplicateElement(0),
90          () => insertRandomValue(1),
91          () => insertRandomValue(1),
92          () => insertRandomValue(0),
93          () => insertHole(1),
94          () => insertHole(0),
95          () => removeElements(1),
96          () => removeElements(elements.length),
97          shuffle,
98        ];
99
100        // Perform several mutations.
101        const count = random.single(MUTATION_CHOICES);
102        for (let i = 0; i < count; i++) {
103          random.single(mutations)();
104        }
105
106        // Don't recurse on nested arrays.
107        path.skip();
108      },
109    }
110  }
111}
112
113module.exports = {
114  ArrayMutator: ArrayMutator,
115};
116