• 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
7 */
8'use strict';
9
10const babelTraverse = require('@babel/traverse').default;
11const babelTypes = require('@babel/types');
12
13class Mutator {
14  get visitor() {
15    return null;
16  }
17
18  _traverse(ast, visitor) {
19    let oldEnter = null;
20    if (Object.prototype.hasOwnProperty.call(visitor, 'enter')) {
21      oldEnter = visitor['enter'];
22    }
23
24    // Transparently skip nodes that are marked.
25    visitor['enter'] = (path) => {
26      if (this.shouldSkip(path.node)) {
27        path.skip();
28        return;
29      }
30
31      if (oldEnter) {
32        oldEnter(path);
33      }
34    }
35
36    babelTraverse(ast, visitor);
37  }
38
39  mutate(source) {
40    if (Array.isArray(this.visitor)) {
41      for (const visitor of this.visitor) {
42        this._traverse(source.ast, visitor);
43      }
44    } else {
45      this._traverse(source.ast, this.visitor);
46    }
47  }
48
49  get _skipPropertyName() {
50    return '__skip' + this.constructor.name;
51  }
52
53  shouldSkip(node) {
54    return Boolean(node[this._skipPropertyName]);
55  }
56
57  skipMutations(node) {
58    // Mark a node to skip further mutations of the same kind.
59    if (Array.isArray(node)) {
60      for (const item of node) {
61        item[this._skipPropertyName] = true;
62      }
63    } else {
64      node[this._skipPropertyName] = true;
65    }
66
67    return node;
68  }
69
70  insertBeforeSkip(path, node) {
71    this.skipMutations(node);
72    path.insertBefore(node);
73  }
74
75  insertAfterSkip(path, node) {
76    this.skipMutations(node);
77    path.insertAfter(node);
78  }
79
80  replaceWithSkip(path, node) {
81    this.skipMutations(node);
82    path.replaceWith(node);
83  }
84
85  replaceWithMultipleSkip(path, node) {
86    this.skipMutations(node);
87    path.replaceWithMultiple(node);
88  }
89
90  annotate(node, message) {
91    babelTypes.addComment(
92        node, 'leading', ` ${this.constructor.name}: ${message} `);
93  }
94}
95
96module.exports = {
97  Mutator: Mutator,
98}
99