• 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 Variables mutator.
7 */
8
9'use strict';
10
11const babelTemplate = require('@babel/template').default;
12const babelTypes = require('@babel/types');
13
14const common = require('./common.js');
15const random = require('../random.js');
16const mutator = require('./mutator.js');
17
18const MAX_MUTATION_RECURSION_DEPTH = 5;
19
20class VariableOrObjectMutator extends mutator.Mutator {
21  constructor(settings) {
22    super();
23    this.settings = settings;
24  }
25
26  _randomVariableOrObject(path) {
27    const randomVar = common.randomVariable(path);
28    if (random.choose(0.05) || !randomVar) {
29      return common.randomObject();
30    }
31
32    return randomVar;
33  }
34
35  _randomVariableOrObjectMutations(path, recurseDepth=0) {
36    if (recurseDepth >= MAX_MUTATION_RECURSION_DEPTH) {
37      return new Array();
38    }
39
40    const probability = random.random();
41
42    if (probability < 0.3) {
43      const first = this._randomVariableOrObjectMutations(path, recurseDepth + 1);
44      const second = this._randomVariableOrObjectMutations(
45          path, recurseDepth + 1);
46      return first.concat(second);
47    }
48
49    const randVarOrObject = this._randomVariableOrObject(path);
50    const randProperty = common.randomProperty(randVarOrObject);
51    let newRandVarOrObject = randVarOrObject;
52    if (random.choose(0.2)) {
53      newRandVarOrObject = this._randomVariableOrObject(path);
54    }
55
56    const mutations = new Array();
57
58    if (probability < 0.4) {
59      const template = babelTemplate(
60          'delete IDENTIFIER[PROPERTY], __callGC()')
61      mutations.push(template({
62        IDENTIFIER: randVarOrObject,
63        PROPERTY: randProperty
64      }));
65    } else if (probability < 0.5) {
66      const template = babelTemplate(
67          'IDENTIFIER[PROPERTY], __callGC()')
68      mutations.push(template({
69        IDENTIFIER: randVarOrObject,
70        PROPERTY: randProperty
71      }));
72    } else if (probability < 0.6) {
73      const template = babelTemplate(
74          'IDENTIFIER[PROPERTY] = RANDOM, __callGC()')
75      mutations.push(template({
76        IDENTIFIER: randVarOrObject,
77        PROPERTY: randProperty,
78        RANDOM: common.randomValue(path),
79      }));
80    } else if (probability < 0.7) {
81      mutations.push(
82          babelTypes.expressionStatement(
83              common.callRandomFunction(path, randVarOrObject)));
84    } else if (probability < 0.8) {
85      const template = babelTemplate(
86          'VAR = IDENTIFIER, __callGC()')
87      var randomVar = common.randomVariable(path);
88      if (!randomVar) {
89        return mutations;
90      }
91
92      mutations.push(template({
93        VAR: randomVar,
94        IDENTIFIER: randVarOrObject,
95      }));
96    } else if (probability < 0.9) {
97      const template = babelTemplate(
98          'if (IDENTIFIER != null && typeof(IDENTIFIER) == "object") ' +
99          'Object.defineProperty(IDENTIFIER, PROPERTY, {value: VALUE})')
100      mutations.push(template({
101          IDENTIFIER: newRandVarOrObject,
102          PROPERTY: randProperty,
103          VALUE: common.randomValue(path),
104      }));
105    } else {
106      const template = babelTemplate(
107          'if (IDENTIFIER != null && typeof(IDENTIFIER) == "object") ' +
108          'Object.defineProperty(IDENTIFIER, PROPERTY, {' +
109          'get: function() { GETTER_MUTATION ; return VALUE; },' +
110          'set: function(value) { SETTER_MUTATION; }' +
111          '})');
112      mutations.push(template({
113          IDENTIFIER: newRandVarOrObject,
114          PROPERTY: randProperty,
115          GETTER_MUTATION: this._randomVariableOrObjectMutations(
116              path, recurseDepth + 1),
117          SETTER_MUTATION: this._randomVariableOrObjectMutations(
118              path, recurseDepth + 1),
119          VALUE: common.randomValue(path),
120      }));
121    }
122
123    return mutations;
124  }
125
126
127  get visitor() {
128    const settings = this.settings;
129    const thisMutator = this;
130
131    return {
132      ExpressionStatement(path) {
133        if (!random.choose(settings.ADD_VAR_OR_OBJ_MUTATIONS)) {
134          return;
135        }
136
137        const mutations = thisMutator._randomVariableOrObjectMutations(path);
138        thisMutator.annotate(mutations[0], 'Random mutation');
139
140        if (random.choose(0.5)) {
141          thisMutator.insertBeforeSkip(path, mutations);
142        } else {
143          thisMutator.insertAfterSkip(path, mutations);
144        }
145
146        path.skip();
147      }
148    };
149  }
150}
151
152module.exports = {
153  VariableOrObjectMutator: VariableOrObjectMutator,
154};
155