// 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, };