tor-browser

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

buildGeneratedBindingList.js (4436B)


      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 { clientCommands } from "../../../client/firefox";
      6 
      7 import { locColumn } from "./locColumn";
      8 import { getOptimizedOutGrip } from "./optimizedOut";
      9 
     10 export function buildGeneratedBindingList(
     11  scopes,
     12  generatedAstScopes,
     13  thisBinding
     14 ) {
     15  // The server's binding data doesn't include general 'this' binding
     16  // information, so we manually inject the one 'this' binding we have into
     17  // the normal binding data we are working with.
     18  const frameThisOwner = generatedAstScopes.find(
     19    generated => "this" in generated.bindings
     20  );
     21 
     22  let globalScope = null;
     23  const clientScopes = [];
     24  for (let s = scopes; s; s = s.parent) {
     25    const bindings = s.bindings
     26      ? Object.assign({}, ...s.bindings.arguments, s.bindings.variables)
     27      : {};
     28 
     29    clientScopes.push(bindings);
     30    globalScope = s;
     31  }
     32 
     33  const generatedMainScopes = generatedAstScopes.slice(0, -2);
     34  const generatedGlobalScopes = generatedAstScopes.slice(-2);
     35 
     36  const clientMainScopes = clientScopes.slice(0, generatedMainScopes.length);
     37  const clientGlobalScopes = clientScopes.slice(generatedMainScopes.length);
     38 
     39  // Map the main parsed script body using the nesting hierarchy of the
     40  // generated and client scopes.
     41  const generatedBindings = generatedMainScopes.reduce((acc, generated, i) => {
     42    const bindings = clientMainScopes[i];
     43 
     44    if (generated === frameThisOwner && thisBinding) {
     45      bindings.this = {
     46        value: thisBinding,
     47      };
     48    }
     49 
     50    for (const name of Object.keys(generated.bindings)) {
     51      // If there is no 'this' value, we exclude the binding entirely.
     52      // Otherwise it would pass through as found, but "(unscoped)", causing
     53      // the search logic to stop with a match.
     54      if (name === "this" && !bindings[name]) {
     55        continue;
     56      }
     57 
     58      const { refs } = generated.bindings[name];
     59      for (const loc of refs) {
     60        acc.push({
     61          name,
     62          loc,
     63          desc: () => Promise.resolve(bindings[name] || null),
     64        });
     65      }
     66    }
     67    return acc;
     68  }, []);
     69 
     70  // Bindings in the global/lexical global of the generated code may or
     71  // may not be the real global if the generated code is running inside
     72  // of an evaled context. To handle this, we just look up the client scope
     73  // hierarchy to find the closest binding with that name.
     74  for (const generated of generatedGlobalScopes) {
     75    for (const name of Object.keys(generated.bindings)) {
     76      const { refs } = generated.bindings[name];
     77      const bindings = clientGlobalScopes.find(b => name in b);
     78 
     79      for (const loc of refs) {
     80        if (bindings) {
     81          generatedBindings.push({
     82            name,
     83            loc,
     84            desc: () => Promise.resolve(bindings[name]),
     85          });
     86        } else {
     87          const globalGrip = globalScope?.object;
     88          if (globalGrip) {
     89            // Should always exist, just checking to keep Flow happy.
     90 
     91            generatedBindings.push({
     92              name,
     93              loc,
     94              desc: async () => {
     95                const objectFront =
     96                  clientCommands.createObjectFront(globalGrip);
     97                return (await objectFront.getProperty(name)).descriptor;
     98              },
     99            });
    100          }
    101        }
    102      }
    103    }
    104  }
    105 
    106  // Sort so we can binary-search.
    107  return sortBindings(generatedBindings);
    108 }
    109 
    110 export function buildFakeBindingList(generatedAstScopes) {
    111  // TODO if possible, inject real bindings for the global scope
    112  const generatedBindings = generatedAstScopes.reduce((acc, generated) => {
    113    for (const name of Object.keys(generated.bindings)) {
    114      if (name === "this") {
    115        continue;
    116      }
    117      const { refs } = generated.bindings[name];
    118      for (const loc of refs) {
    119        acc.push({
    120          name,
    121          loc,
    122          desc: () => Promise.resolve(getOptimizedOutGrip()),
    123        });
    124      }
    125    }
    126    return acc;
    127  }, []);
    128  return sortBindings(generatedBindings);
    129 }
    130 
    131 function sortBindings(generatedBindings) {
    132  return generatedBindings.sort((a, b) => {
    133    const aStart = a.loc.start;
    134    const bStart = b.loc.start;
    135 
    136    if (aStart.line === bStart.line) {
    137      return locColumn(aStart) - locColumn(bStart);
    138    }
    139    return aStart.line - bStart.line;
    140  });
    141 }