blob: 7b5a35a7b3976051f356ecd71eb32cf6df9664ec [file] [log] [blame]
// 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 Normalizer.
* This renames variables so that we don't have collisions when combining
* different files. It also simplifies other logic when e.g. determining the
* type of an identifier.
*/
'use strict';
const babelTypes = require('@babel/types');
const mutator = require('./mutator.js');
class NormalizerContext {
constructor() {
this.funcIndex = 0;
this.varIndex = 0;
this.classIndex = 0;
}
}
class IdentifierNormalizer extends mutator.Mutator {
constructor() {
super();
this.context = new NormalizerContext();
}
get visitor() {
const context = this.context;
const renamed = new WeakSet();
const globalMappings = new Map();
return [{
Scope(path) {
for (const [name, binding] of Object.entries(path.scope.bindings)) {
if (renamed.has(binding.identifier)) {
continue;
}
renamed.add(binding.identifier);
if (babelTypes.isClassDeclaration(binding.path.node) ||
babelTypes.isClassExpression(binding.path.node)) {
path.scope.rename(name, '__c_' + context.classIndex++);
} else if (babelTypes.isFunctionDeclaration(binding.path.node) ||
babelTypes.isFunctionExpression(binding.path.node)) {
path.scope.rename(name, '__f_' + context.funcIndex++);
} else {
path.scope.rename(name, '__v_' + context.varIndex++);
}
}
},
AssignmentExpression(path) {
// Find assignments for which we have no binding in the scope. We assume
// that these are globals which are local to our script (which weren't
// declared with var/let/const etc).
const ids = path.getBindingIdentifiers();
for (const name in ids) {
if (!path.scope.getBinding(name)) {
globalMappings.set(name, '__v_' + context.varIndex++);
}
}
}
}, {
// Second pass to rename globals that weren't declared with
// var/let/const etc.
Identifier(path) {
if (!globalMappings.has(path.node.name)) {
return;
}
if (path.scope.getBinding(path.node.name)) {
// Don't rename if there is a binding that hides the global.
return;
}
path.node.name = globalMappings.get(path.node.name);
}
}];
}
}
module.exports = {
IdentifierNormalizer: IdentifierNormalizer,
};