tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

visitor.js (28352B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
      4 
      5 import * as t from "@babel/types";
      6 
      7 import getFunctionName from "../utils/getFunctionName";
      8 import { getAst } from "../utils/ast";
      9 
     10 /**
     11 * "implicit"
     12 * Variables added automaticly like "this" and "arguments"
     13 *
     14 * "var"
     15 * Variables declared with "var" or non-block function declarations
     16 *
     17 * "let"
     18 * Variables declared with "let".
     19 *
     20 * "const"
     21 * Variables declared with "const", or added as const
     22 * bindings like inner function expressions and inner class names.
     23 *
     24 * "import"
     25 * Imported binding names exposed from other modules.
     26 *
     27 * "global"
     28 * Variables that reference undeclared global values.
     29 */
     30 
     31 // Location information about the expression immediartely surrounding a
     32 // given binding reference.
     33 
     34 function isGeneratedId(id) {
     35  return !/\/originalSource/.test(id);
     36 }
     37 
     38 export function parseSourceScopes(sourceId) {
     39  const ast = getAst(sourceId);
     40  if (!ast || !Object.keys(ast).length) {
     41    return null;
     42  }
     43 
     44  return buildScopeList(ast, sourceId);
     45 }
     46 
     47 export function buildScopeList(ast, sourceId) {
     48  const { global, lexical } = createGlobalScope(ast, sourceId);
     49 
     50  const state = {
     51    // The id for the source that scope list is generated for
     52    sourceId,
     53 
     54    // A map of any free variables(variables which are used within the current scope but not
     55    // declared within the scope). This changes when a new scope is created.
     56    freeVariables: new Map(),
     57 
     58    // A stack of all the free variables created across all the scopes that have
     59    // been created.
     60    freeVariableStack: [],
     61 
     62    inType: null,
     63 
     64    // The current scope, a new scope is potentially created on a visit to each node
     65    // depending in the criteria. Initially set to the lexical global scope which is the
     66    // child to the global scope.
     67    scope: lexical,
     68 
     69    // A stack of all the existing scopes, this is mainly used retrieve the parent scope
     70    // (which is the last scope push onto the stack) on exiting a visited node.
     71    scopeStack: [],
     72 
     73    declarationBindingIds: new Set(),
     74  };
     75  t.traverse(ast, scopeCollectionVisitor, state);
     76 
     77  for (const [key, freeVariables] of state.freeVariables) {
     78    let binding = global.bindings[key];
     79    if (!binding) {
     80      binding = {
     81        type: "global",
     82        refs: [],
     83      };
     84      global.bindings[key] = binding;
     85    }
     86 
     87    binding.refs = freeVariables.concat(binding.refs);
     88  }
     89 
     90  // TODO: This should probably check for ".mjs" extension on the
     91  // original file, and should also be skipped if the the generated
     92  // code is an ES6 module rather than a script.
     93  if (
     94    isGeneratedId(sourceId) ||
     95    (ast.program.sourceType === "script" && !looksLikeCommonJS(global))
     96  ) {
     97    stripModuleScope(global);
     98  }
     99 
    100  return toParsedScopes([global], sourceId) || [];
    101 }
    102 
    103 function toParsedScopes(children, sourceId) {
    104  if (!children || children.length === 0) {
    105    return undefined;
    106  }
    107  return children.map(scope => ({
    108    // Removing unneed information from TempScope such as parent reference.
    109    // We also need to convert BabelLocation to the Location type.
    110    start: scope.loc.start,
    111    end: scope.loc.end,
    112    type:
    113      scope.type === "module" || scope.type === "function-body"
    114        ? "block"
    115        : scope.type,
    116    scopeKind: "",
    117    displayName: scope.displayName,
    118    bindings: scope.bindings,
    119    children: toParsedScopes(scope.children, sourceId),
    120  }));
    121 }
    122 
    123 /**
    124 * Create a new scope object and link the scope to it parent.
    125 *
    126 * @param {string} type - scope type
    127 * @param {string} displayName - The scope display name
    128 * @param {object} parent - The parent object scope
    129 * @param {object} loc - The start and end postions (line/columns) of the scope
    130 * @returns {object} The newly created scope
    131 */
    132 function createTempScope(type, displayName, parent, loc) {
    133  const scope = {
    134    type,
    135    displayName,
    136    parent,
    137 
    138    // A list of all the child scopes
    139    children: [],
    140    loc,
    141 
    142    // All the bindings defined in this scope
    143    // bindings = [binding, ...]
    144    // binding = { type: "", refs: []}
    145    bindings: Object.create(null),
    146  };
    147 
    148  if (parent) {
    149    parent.children.push(scope);
    150  }
    151  return scope;
    152 }
    153 
    154 // Sets a new current scope and creates a new map to store the free variables
    155 // that may exist in this scope.
    156 function pushTempScope(state, type, displayName, loc) {
    157  const scope = createTempScope(type, displayName, state.scope, loc);
    158 
    159  state.scope = scope;
    160 
    161  state.freeVariableStack.push(state.freeVariables);
    162  state.freeVariables = new Map();
    163  return scope;
    164 }
    165 
    166 function isNode(node, type) {
    167  return node ? node.type === type : false;
    168 }
    169 
    170 // Walks up the scope tree to the top most variable scope
    171 function getVarScope(scope) {
    172  let s = scope;
    173  while (s.type !== "function" && s.type !== "module") {
    174    if (!s.parent) {
    175      return s;
    176    }
    177    s = s.parent;
    178  }
    179  return s;
    180 }
    181 
    182 function fromBabelLocation(location, sourceId) {
    183  return {
    184    sourceId,
    185    line: location.line,
    186    column: location.column,
    187  };
    188 }
    189 
    190 function parseDeclarator(
    191  declaratorId,
    192  targetScope,
    193  type,
    194  locationType,
    195  declaration,
    196  state
    197 ) {
    198  if (isNode(declaratorId, "Identifier")) {
    199    let existing = targetScope.bindings[declaratorId.name];
    200    if (!existing) {
    201      existing = {
    202        type,
    203        refs: [],
    204      };
    205      targetScope.bindings[declaratorId.name] = existing;
    206    }
    207    state.declarationBindingIds.add(declaratorId);
    208    existing.refs.push({
    209      type: locationType,
    210      start: fromBabelLocation(declaratorId.loc.start, state.sourceId),
    211      end: fromBabelLocation(declaratorId.loc.end, state.sourceId),
    212      declaration: {
    213        start: fromBabelLocation(declaration.loc.start, state.sourceId),
    214        end: fromBabelLocation(declaration.loc.end, state.sourceId),
    215      },
    216    });
    217  } else if (isNode(declaratorId, "ObjectPattern")) {
    218    declaratorId.properties.forEach(prop => {
    219      parseDeclarator(
    220        prop.value,
    221        targetScope,
    222        type,
    223        locationType,
    224        declaration,
    225        state
    226      );
    227    });
    228  } else if (isNode(declaratorId, "ArrayPattern")) {
    229    declaratorId.elements.forEach(item => {
    230      parseDeclarator(
    231        item,
    232        targetScope,
    233        type,
    234        locationType,
    235        declaration,
    236        state
    237      );
    238    });
    239  } else if (isNode(declaratorId, "AssignmentPattern")) {
    240    parseDeclarator(
    241      declaratorId.left,
    242      targetScope,
    243      type,
    244      locationType,
    245      declaration,
    246      state
    247    );
    248  } else if (isNode(declaratorId, "RestElement")) {
    249    parseDeclarator(
    250      declaratorId.argument,
    251      targetScope,
    252      type,
    253      locationType,
    254      declaration,
    255      state
    256    );
    257  } else if (t.isTSParameterProperty(declaratorId)) {
    258    parseDeclarator(
    259      declaratorId.parameter,
    260      targetScope,
    261      type,
    262      locationType,
    263      declaration,
    264      state
    265    );
    266  }
    267 }
    268 
    269 function isLetOrConst(node) {
    270  return node.kind === "let" || node.kind === "const";
    271 }
    272 
    273 function hasLexicalDeclaration(node, parent) {
    274  const nodes = [];
    275  if (t.isSwitchStatement(node)) {
    276    for (const caseNode of node.cases) {
    277      nodes.push(...caseNode.consequent);
    278    }
    279  } else {
    280    nodes.push(...node.body);
    281  }
    282 
    283  const isFunctionBody = t.isFunction(parent, { body: node });
    284 
    285  return nodes.some(
    286    child =>
    287      isLexicalVariable(child) ||
    288      t.isClassDeclaration(child) ||
    289      (!isFunctionBody && t.isFunctionDeclaration(child))
    290  );
    291 }
    292 function isLexicalVariable(node) {
    293  return isNode(node, "VariableDeclaration") && isLetOrConst(node);
    294 }
    295 
    296 // Creates the global scopes for this source, the overall global scope
    297 // and a lexical global scope.
    298 function createGlobalScope(ast, sourceId) {
    299  const global = createTempScope("object", "Global", null, {
    300    start: fromBabelLocation(ast.loc.start, sourceId),
    301    end: fromBabelLocation(ast.loc.end, sourceId),
    302  });
    303 
    304  const lexical = createTempScope("block", "Lexical Global", global, {
    305    start: fromBabelLocation(ast.loc.start, sourceId),
    306    end: fromBabelLocation(ast.loc.end, sourceId),
    307  });
    308 
    309  return { global, lexical };
    310 }
    311 
    312 const scopeCollectionVisitor = {
    313  // eslint-disable-next-line complexity
    314  enter(node, ancestors, state) {
    315    state.scopeStack.push(state.scope);
    316 
    317    const parentNode =
    318      ancestors.length === 0 ? null : ancestors[ancestors.length - 1].node;
    319 
    320    if (state.inType) {
    321      return;
    322    }
    323 
    324    if (t.isProgram(node)) {
    325      const scope = pushTempScope(state, "module", "Module", {
    326        start: fromBabelLocation(node.loc.start, state.sourceId),
    327        end: fromBabelLocation(node.loc.end, state.sourceId),
    328      });
    329      scope.bindings.this = {
    330        type: "implicit",
    331        refs: [],
    332      };
    333    } else if (t.isFunction(node)) {
    334      let { scope } = state;
    335 
    336      if (t.isFunctionExpression(node) && isNode(node.id, "Identifier")) {
    337        scope = pushTempScope(state, "block", "Function Expression", {
    338          start: fromBabelLocation(node.loc.start, state.sourceId),
    339          end: fromBabelLocation(node.loc.end, state.sourceId),
    340        });
    341        state.declarationBindingIds.add(node.id);
    342        scope.bindings[node.id.name] = {
    343          type: "const",
    344          refs: [
    345            {
    346              type: "fn-expr",
    347              start: fromBabelLocation(node.id.loc.start, state.sourceId),
    348              end: fromBabelLocation(node.id.loc.end, state.sourceId),
    349              declaration: {
    350                start: fromBabelLocation(node.loc.start, state.sourceId),
    351                end: fromBabelLocation(node.loc.end, state.sourceId),
    352              },
    353            },
    354          ],
    355        };
    356      }
    357 
    358      if (t.isFunctionDeclaration(node) && isNode(node.id, "Identifier")) {
    359        // This ignores Annex B function declaration hoisting, which
    360        // is probably a fine assumption.
    361        state.declarationBindingIds.add(node.id);
    362        const refs = [
    363          {
    364            type: "fn-decl",
    365            start: fromBabelLocation(node.id.loc.start, state.sourceId),
    366            end: fromBabelLocation(node.id.loc.end, state.sourceId),
    367            declaration: {
    368              start: fromBabelLocation(node.loc.start, state.sourceId),
    369              end: fromBabelLocation(node.loc.end, state.sourceId),
    370            },
    371          },
    372        ];
    373 
    374        if (scope.type === "block") {
    375          scope.bindings[node.id.name] = {
    376            type: "let",
    377            refs,
    378          };
    379        } else {
    380          // Add the binding to the ancestor scope
    381          getVarScope(scope).bindings[node.id.name] = {
    382            type: "var",
    383            refs,
    384          };
    385        }
    386      }
    387 
    388      scope = pushTempScope(
    389        state,
    390        "function",
    391        getFunctionName(node, parentNode),
    392        {
    393          // Being at the start of a function doesn't count as
    394          // being inside of it.
    395          start: fromBabelLocation(
    396            node.params[0] ? node.params[0].loc.start : node.loc.start,
    397            state.sourceId
    398          ),
    399          end: fromBabelLocation(node.loc.end, state.sourceId),
    400        }
    401      );
    402 
    403      node.params.forEach(param =>
    404        parseDeclarator(param, scope, "var", "fn-param", node, state)
    405      );
    406 
    407      if (!t.isArrowFunctionExpression(node)) {
    408        scope.bindings.this = {
    409          type: "implicit",
    410          refs: [],
    411        };
    412        scope.bindings.arguments = {
    413          type: "implicit",
    414          refs: [],
    415        };
    416      }
    417 
    418      if (
    419        t.isBlockStatement(node.body) &&
    420        hasLexicalDeclaration(node.body, node)
    421      ) {
    422        scope = pushTempScope(state, "function-body", "Function Body", {
    423          start: fromBabelLocation(node.body.loc.start, state.sourceId),
    424          end: fromBabelLocation(node.body.loc.end, state.sourceId),
    425        });
    426      }
    427    } else if (t.isClass(node)) {
    428      if (t.isIdentifier(node.id)) {
    429        // For decorated classes, the AST considers the first the decorator
    430        // to be the start of the class. For the purposes of mapping class
    431        // declarations however, we really want to look for the "class Foo"
    432        // piece. To achieve that, we estimate the location of the declaration
    433        // instead.
    434        let declStart = node.loc.start;
    435        if (node.decorators && node.decorators.length) {
    436          // Estimate the location of the "class" keyword since it
    437          // is unlikely to be a different line than the class name.
    438          declStart = {
    439            line: node.id.loc.start.line,
    440            column: node.id.loc.start.column - "class ".length,
    441          };
    442        }
    443 
    444        const declaration = {
    445          start: fromBabelLocation(declStart, state.sourceId),
    446          end: fromBabelLocation(node.loc.end, state.sourceId),
    447        };
    448 
    449        if (t.isClassDeclaration(node)) {
    450          state.declarationBindingIds.add(node.id);
    451          state.scope.bindings[node.id.name] = {
    452            type: "let",
    453            refs: [
    454              {
    455                type: "class-decl",
    456                start: fromBabelLocation(node.id.loc.start, state.sourceId),
    457                end: fromBabelLocation(node.id.loc.end, state.sourceId),
    458                declaration,
    459              },
    460            ],
    461          };
    462        }
    463 
    464        const scope = pushTempScope(state, "block", "Class", {
    465          start: fromBabelLocation(node.loc.start, state.sourceId),
    466          end: fromBabelLocation(node.loc.end, state.sourceId),
    467        });
    468 
    469        state.declarationBindingIds.add(node.id);
    470        scope.bindings[node.id.name] = {
    471          type: "const",
    472          refs: [
    473            {
    474              type: "class-inner",
    475              start: fromBabelLocation(node.id.loc.start, state.sourceId),
    476              end: fromBabelLocation(node.id.loc.end, state.sourceId),
    477              declaration,
    478            },
    479          ],
    480        };
    481      }
    482    } else if (t.isForXStatement(node) || t.isForStatement(node)) {
    483      const init = node.init || node.left;
    484      if (isNode(init, "VariableDeclaration") && isLetOrConst(init)) {
    485        // Debugger will create new lexical environment for the for.
    486        pushTempScope(state, "block", "For", {
    487          // Being at the start of a for loop doesn't count as
    488          // being inside it.
    489          start: fromBabelLocation(init.loc.start, state.sourceId),
    490          end: fromBabelLocation(node.loc.end, state.sourceId),
    491        });
    492      }
    493    } else if (t.isCatchClause(node)) {
    494      const scope = pushTempScope(state, "block", "Catch", {
    495        start: fromBabelLocation(node.loc.start, state.sourceId),
    496        end: fromBabelLocation(node.loc.end, state.sourceId),
    497      });
    498      parseDeclarator(node.param, scope, "var", "catch", node, state);
    499    } else if (
    500      t.isBlockStatement(node) &&
    501      // Function body's are handled in the function logic above.
    502      !t.isFunction(parentNode) &&
    503      hasLexicalDeclaration(node, parentNode)
    504    ) {
    505      // Debugger will create new lexical environment for the block.
    506      pushTempScope(state, "block", "Block", {
    507        start: fromBabelLocation(node.loc.start, state.sourceId),
    508        end: fromBabelLocation(node.loc.end, state.sourceId),
    509      });
    510    } else if (
    511      t.isVariableDeclaration(node) &&
    512      (node.kind === "var" ||
    513        // Lexical declarations in for statements are handled above.
    514        !t.isForStatement(parentNode, { init: node }) ||
    515        !t.isForXStatement(parentNode, { left: node }))
    516    ) {
    517      // Finds right lexical environment
    518      const hoistAt = !isLetOrConst(node)
    519        ? getVarScope(state.scope)
    520        : state.scope;
    521      node.declarations.forEach(declarator => {
    522        parseDeclarator(
    523          declarator.id,
    524          hoistAt,
    525          node.kind,
    526          node.kind,
    527          node,
    528          state
    529        );
    530      });
    531    } else if (
    532      t.isImportDeclaration(node) &&
    533      (!node.importKind || node.importKind === "value")
    534    ) {
    535      node.specifiers.forEach(spec => {
    536        if (spec.importKind && spec.importKind !== "value") {
    537          return;
    538        }
    539 
    540        if (t.isImportNamespaceSpecifier(spec)) {
    541          state.declarationBindingIds.add(spec.local);
    542 
    543          state.scope.bindings[spec.local.name] = {
    544            // Imported namespaces aren't live import bindings, they are
    545            // just normal const bindings.
    546            type: "const",
    547            refs: [
    548              {
    549                type: "import-ns-decl",
    550                start: fromBabelLocation(spec.local.loc.start, state.sourceId),
    551                end: fromBabelLocation(spec.local.loc.end, state.sourceId),
    552                declaration: {
    553                  start: fromBabelLocation(node.loc.start, state.sourceId),
    554                  end: fromBabelLocation(node.loc.end, state.sourceId),
    555                },
    556              },
    557            ],
    558          };
    559        } else {
    560          state.declarationBindingIds.add(spec.local);
    561 
    562          state.scope.bindings[spec.local.name] = {
    563            type: "import",
    564            refs: [
    565              {
    566                type: "import-decl",
    567                start: fromBabelLocation(spec.local.loc.start, state.sourceId),
    568                end: fromBabelLocation(spec.local.loc.end, state.sourceId),
    569                importName: t.isImportDefaultSpecifier(spec)
    570                  ? "default"
    571                  : spec.imported.name,
    572                declaration: {
    573                  start: fromBabelLocation(node.loc.start, state.sourceId),
    574                  end: fromBabelLocation(node.loc.end, state.sourceId),
    575                },
    576              },
    577            ],
    578          };
    579        }
    580      });
    581    } else if (t.isTSEnumDeclaration(node)) {
    582      state.declarationBindingIds.add(node.id);
    583      state.scope.bindings[node.id.name] = {
    584        type: "const",
    585        refs: [
    586          {
    587            type: "ts-enum-decl",
    588            start: fromBabelLocation(node.id.loc.start, state.sourceId),
    589            end: fromBabelLocation(node.id.loc.end, state.sourceId),
    590            declaration: {
    591              start: fromBabelLocation(node.loc.start, state.sourceId),
    592              end: fromBabelLocation(node.loc.end, state.sourceId),
    593            },
    594          },
    595        ],
    596      };
    597    } else if (t.isTSModuleDeclaration(node)) {
    598      state.declarationBindingIds.add(node.id);
    599      state.scope.bindings[node.id.name] = {
    600        type: "const",
    601        refs: [
    602          {
    603            type: "ts-namespace-decl",
    604            start: fromBabelLocation(node.id.loc.start, state.sourceId),
    605            end: fromBabelLocation(node.id.loc.end, state.sourceId),
    606            declaration: {
    607              start: fromBabelLocation(node.loc.start, state.sourceId),
    608              end: fromBabelLocation(node.loc.end, state.sourceId),
    609            },
    610          },
    611        ],
    612      };
    613    } else if (t.isTSModuleBlock(node)) {
    614      pushTempScope(state, "block", "TypeScript Namespace", {
    615        start: fromBabelLocation(node.loc.start, state.sourceId),
    616        end: fromBabelLocation(node.loc.end, state.sourceId),
    617      });
    618    } else if (
    619      t.isIdentifier(node) &&
    620      t.isReferenced(node, parentNode) &&
    621      // Babel doesn't cover this in 'isReferenced' yet, but it should
    622      // eventually.
    623      !t.isTSEnumMember(parentNode, { id: node }) &&
    624      !t.isTSModuleDeclaration(parentNode, { id: node }) &&
    625      // isReferenced above fails to see `var { foo } = ...` as a non-reference
    626      // because the direct parent is not enough to know that the pattern is
    627      // used within a variable declaration.
    628      !state.declarationBindingIds.has(node)
    629    ) {
    630      let freeVariables = state.freeVariables.get(node.name);
    631      if (!freeVariables) {
    632        freeVariables = [];
    633        state.freeVariables.set(node.name, freeVariables);
    634      }
    635 
    636      freeVariables.push({
    637        type: "ref",
    638        start: fromBabelLocation(node.loc.start, state.sourceId),
    639        end: fromBabelLocation(node.loc.end, state.sourceId),
    640        meta: buildMetaBindings(state.sourceId, node, ancestors),
    641      });
    642    } else if (isOpeningJSXIdentifier(node, ancestors)) {
    643      let freeVariables = state.freeVariables.get(node.name);
    644      if (!freeVariables) {
    645        freeVariables = [];
    646        state.freeVariables.set(node.name, freeVariables);
    647      }
    648 
    649      freeVariables.push({
    650        type: "ref",
    651        start: fromBabelLocation(node.loc.start, state.sourceId),
    652        end: fromBabelLocation(node.loc.end, state.sourceId),
    653        meta: buildMetaBindings(state.sourceId, node, ancestors),
    654      });
    655    } else if (t.isThisExpression(node)) {
    656      let freeVariables = state.freeVariables.get("this");
    657      if (!freeVariables) {
    658        freeVariables = [];
    659        state.freeVariables.set("this", freeVariables);
    660      }
    661 
    662      freeVariables.push({
    663        type: "ref",
    664        start: fromBabelLocation(node.loc.start, state.sourceId),
    665        end: fromBabelLocation(node.loc.end, state.sourceId),
    666        meta: buildMetaBindings(state.sourceId, node, ancestors),
    667      });
    668    } else if (t.isClassProperty(parentNode, { value: node })) {
    669      const scope = pushTempScope(state, "function", "Class Field", {
    670        start: fromBabelLocation(node.loc.start, state.sourceId),
    671        end: fromBabelLocation(node.loc.end, state.sourceId),
    672      });
    673      scope.bindings.this = {
    674        type: "implicit",
    675        refs: [],
    676      };
    677      scope.bindings.arguments = {
    678        type: "implicit",
    679        refs: [],
    680      };
    681    } else if (
    682      t.isSwitchStatement(node) &&
    683      hasLexicalDeclaration(node, parentNode)
    684    ) {
    685      pushTempScope(state, "block", "Switch", {
    686        start: fromBabelLocation(node.loc.start, state.sourceId),
    687        end: fromBabelLocation(node.loc.end, state.sourceId),
    688      });
    689    }
    690 
    691    if (
    692      // In general Flow expressions are deleted, so they can't contain
    693      // runtime bindings, but typecasts are the one exception there.
    694      (t.isFlow(node) && !t.isTypeCastExpression(node)) ||
    695      // In general TS items are deleted, but TS has a few wrapper node
    696      // types that can contain general JS expressions.
    697      (node.type.startsWith("TS") &&
    698        !t.isTSTypeAssertion(node) &&
    699        !t.isTSAsExpression(node) &&
    700        !t.isTSNonNullExpression(node) &&
    701        !t.isTSModuleDeclaration(node) &&
    702        !t.isTSModuleBlock(node) &&
    703        !t.isTSParameterProperty(node) &&
    704        !t.isTSExportAssignment(node))
    705    ) {
    706      // Flag this node as a root "type" node. All items inside of this
    707      // will be skipped entirely.
    708      state.inType = node;
    709    }
    710  },
    711  exit(node, ancestors, state) {
    712    const currentScope = state.scope;
    713    const parentScope = state.scopeStack.pop();
    714    if (!parentScope) {
    715      throw new Error("Assertion failure - unsynchronized pop");
    716    }
    717    state.scope = parentScope;
    718 
    719    // It is possible, as in the case of function expressions, that a single
    720    // node has added multiple scopes, so we need to traverse upward here
    721    // rather than jumping stright to 'parentScope'.
    722    for (
    723      let scope = currentScope;
    724      scope && scope !== parentScope;
    725      scope = scope.parent
    726    ) {
    727      const { freeVariables } = state;
    728      state.freeVariables = state.freeVariableStack.pop();
    729      const parentFreeVariables = state.freeVariables;
    730 
    731      // Match up any free variables that match this scope's bindings and
    732      // merge then into the refs.
    733      for (const key of Object.keys(scope.bindings)) {
    734        const binding = scope.bindings[key];
    735 
    736        const freeVars = freeVariables.get(key);
    737        if (freeVars) {
    738          binding.refs.push(...freeVars);
    739          freeVariables.delete(key);
    740        }
    741      }
    742 
    743      // Move any undeclared references in this scope into the parent for
    744      // processing in higher scopes.
    745      for (const [key, value] of freeVariables) {
    746        let refs = parentFreeVariables.get(key);
    747        if (!refs) {
    748          refs = [];
    749          parentFreeVariables.set(key, refs);
    750        }
    751 
    752        refs.push(...value);
    753      }
    754    }
    755 
    756    if (state.inType === node) {
    757      state.inType = null;
    758    }
    759  },
    760 };
    761 
    762 function isOpeningJSXIdentifier(node, ancestors) {
    763  if (!t.isJSXIdentifier(node)) {
    764    return false;
    765  }
    766 
    767  for (let i = ancestors.length - 1; i >= 0; i--) {
    768    const { node: parent, key } = ancestors[i];
    769 
    770    if (t.isJSXOpeningElement(parent) && key === "name") {
    771      return true;
    772    } else if (!t.isJSXMemberExpression(parent) || key !== "object") {
    773      break;
    774    }
    775  }
    776 
    777  return false;
    778 }
    779 
    780 function buildMetaBindings(
    781  sourceId,
    782  node,
    783  ancestors,
    784  parentIndex = ancestors.length - 1
    785 ) {
    786  if (parentIndex <= 1) {
    787    return null;
    788  }
    789  const parent = ancestors[parentIndex].node;
    790  const grandparent = ancestors[parentIndex - 1].node;
    791 
    792  // Consider "0, foo" to be equivalent to "foo".
    793  if (
    794    t.isSequenceExpression(parent) &&
    795    parent.expressions.length === 2 &&
    796    t.isNumericLiteral(parent.expressions[0]) &&
    797    parent.expressions[1] === node
    798  ) {
    799    let { start, end } = parent.loc;
    800 
    801    if (t.isCallExpression(grandparent, { callee: parent })) {
    802      // Attempt to expand the range around parentheses, e.g.
    803      // (0, foo.bar)()
    804      start = grandparent.loc.start;
    805      end = Object.assign({}, end);
    806      end.column += 1;
    807    }
    808 
    809    return {
    810      type: "inherit",
    811      start: fromBabelLocation(start, sourceId),
    812      end: fromBabelLocation(end, sourceId),
    813      parent: buildMetaBindings(sourceId, parent, ancestors, parentIndex - 1),
    814    };
    815  }
    816 
    817  // Consider "Object(foo)", and "__webpack_require__.i(foo)" to be
    818  // equivalent to "foo" since they are essentially identity functions.
    819  if (
    820    t.isCallExpression(parent) &&
    821    (t.isIdentifier(parent.callee, { name: "Object" }) ||
    822      (t.isMemberExpression(parent.callee, { computed: false }) &&
    823        t.isIdentifier(parent.callee.object, { name: "__webpack_require__" }) &&
    824        t.isIdentifier(parent.callee.property, { name: "i" }))) &&
    825    parent.arguments.length === 1 &&
    826    parent.arguments[0] === node
    827  ) {
    828    return {
    829      type: "inherit",
    830      start: fromBabelLocation(parent.loc.start, sourceId),
    831      end: fromBabelLocation(parent.loc.end, sourceId),
    832      parent: buildMetaBindings(sourceId, parent, ancestors, parentIndex - 1),
    833    };
    834  }
    835 
    836  if (t.isMemberExpression(parent, { object: node })) {
    837    if (parent.computed) {
    838      if (t.isStringLiteral(parent.property)) {
    839        return {
    840          type: "member",
    841          start: fromBabelLocation(parent.loc.start, sourceId),
    842          end: fromBabelLocation(parent.loc.end, sourceId),
    843          property: parent.property.value,
    844          parent: buildMetaBindings(
    845            sourceId,
    846            parent,
    847            ancestors,
    848            parentIndex - 1
    849          ),
    850        };
    851      }
    852    } else {
    853      return {
    854        type: "member",
    855        start: fromBabelLocation(parent.loc.start, sourceId),
    856        end: fromBabelLocation(parent.loc.end, sourceId),
    857        property: parent.property.name,
    858        parent: buildMetaBindings(sourceId, parent, ancestors, parentIndex - 1),
    859      };
    860    }
    861  }
    862  if (
    863    t.isCallExpression(parent, { callee: node }) &&
    864    !parent.arguments.length
    865  ) {
    866    return {
    867      type: "call",
    868      start: fromBabelLocation(parent.loc.start, sourceId),
    869      end: fromBabelLocation(parent.loc.end, sourceId),
    870      parent: buildMetaBindings(sourceId, parent, ancestors, parentIndex - 1),
    871    };
    872  }
    873 
    874  return null;
    875 }
    876 
    877 function looksLikeCommonJS(rootScope) {
    878  const hasRefs = name =>
    879    rootScope.bindings[name] && !!rootScope.bindings[name].refs.length;
    880 
    881  return (
    882    hasRefs("__dirname") ||
    883    hasRefs("__filename") ||
    884    hasRefs("require") ||
    885    hasRefs("exports") ||
    886    hasRefs("module")
    887  );
    888 }
    889 
    890 function stripModuleScope(rootScope) {
    891  const rootLexicalScope = rootScope.children[0];
    892  const moduleScope = rootLexicalScope.children[0];
    893  if (moduleScope.type !== "module") {
    894    throw new Error("Assertion failure - should be module");
    895  }
    896 
    897  Object.keys(moduleScope.bindings).forEach(name => {
    898    const binding = moduleScope.bindings[name];
    899    if (binding.type === "let" || binding.type === "const") {
    900      rootLexicalScope.bindings[name] = binding;
    901    } else {
    902      rootScope.bindings[name] = binding;
    903    }
    904  });
    905  rootLexicalScope.children = moduleScope.children;
    906  rootLexicalScope.children.forEach(child => {
    907    child.parent = rootLexicalScope;
    908  });
    909 }