tor-browser

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

mapBindings.js (3028B)


      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 { replaceNode } from "./utils/ast";
      6 import { isTopLevel } from "./utils/helpers";
      7 
      8 import generate from "@babel/generator";
      9 import * as t from "@babel/types";
     10 
     11 function getAssignmentTarget(node, bindings) {
     12  if (t.isObjectPattern(node)) {
     13    for (const property of node.properties) {
     14      if (t.isRestElement(property)) {
     15        property.argument = getAssignmentTarget(property.argument, bindings);
     16      } else {
     17        property.value = getAssignmentTarget(property.value, bindings);
     18      }
     19    }
     20 
     21    return node;
     22  }
     23 
     24  if (t.isArrayPattern(node)) {
     25    for (const [i, element] of node.elements.entries()) {
     26      node.elements[i] = getAssignmentTarget(element, bindings);
     27    }
     28 
     29    return node;
     30  }
     31 
     32  if (t.isAssignmentPattern(node)) {
     33    node.left = getAssignmentTarget(node.left, bindings);
     34 
     35    return node;
     36  }
     37 
     38  if (t.isRestElement(node)) {
     39    node.argument = getAssignmentTarget(node.argument, bindings);
     40 
     41    return node;
     42  }
     43 
     44  if (t.isIdentifier(node)) {
     45    return bindings.includes(node.name)
     46      ? node
     47      : t.memberExpression(t.identifier("self"), node);
     48  }
     49 
     50  return node;
     51 }
     52 
     53 // translates new bindings `var a = 3` into `self.a = 3`
     54 // and existing bindings `var a = 3` into `a = 3` for re-assignments
     55 function globalizeDeclaration(node, bindings) {
     56  return node.declarations.map(declaration =>
     57    t.expressionStatement(
     58      t.assignmentExpression(
     59        "=",
     60        getAssignmentTarget(declaration.id, bindings),
     61        declaration.init || t.unaryExpression("void", t.numericLiteral(0))
     62      )
     63    )
     64  );
     65 }
     66 
     67 // translates new bindings `a = 3` into `self.a = 3`
     68 // and keeps assignments the same for existing bindings.
     69 function globalizeAssignment(node, bindings) {
     70  return t.assignmentExpression(
     71    node.operator,
     72    getAssignmentTarget(node.left, bindings),
     73    node.right
     74  );
     75 }
     76 
     77 export default function mapExpressionBindings(expression, ast, bindings = []) {
     78  let isMapped = false;
     79  let shouldUpdate = true;
     80 
     81  t.traverse(ast, (node, ancestors) => {
     82    const parent = ancestors[ancestors.length - 1];
     83 
     84    if (t.isWithStatement(node)) {
     85      shouldUpdate = false;
     86      return;
     87    }
     88 
     89    if (!isTopLevel(ancestors)) {
     90      return;
     91    }
     92 
     93    if (t.isAssignmentExpression(node)) {
     94      if (t.isIdentifier(node.left) || t.isPattern(node.left)) {
     95        const newNode = globalizeAssignment(node, bindings);
     96        isMapped = true;
     97        replaceNode(ancestors, newNode);
     98        return;
     99      }
    100 
    101      return;
    102    }
    103 
    104    if (!t.isVariableDeclaration(node)) {
    105      return;
    106    }
    107 
    108    if (!t.isForStatement(parent.node)) {
    109      const newNodes = globalizeDeclaration(node, bindings);
    110      isMapped = true;
    111      replaceNode(ancestors, newNodes);
    112    }
    113  });
    114 
    115  if (!shouldUpdate || !isMapped) {
    116    return expression;
    117  }
    118 
    119  return generate(ast).code;
    120 }