/**
Copyright: 2024 Contrast Security, Inc
  Contact: support@contrastsecurity.com
  License: Commercial

  NOTICE: This Software and the patented inventions embodied within may only be
  used as part of Contrast Security’s commercial offerings. Even though it is
  made available through public repositories, use of this Software is subject to
  the applicable End User Licensing Agreement found at
  https://www.contrastsecurity.com/enduser-terms-0317a or as otherwise agreed
  between Contrast Security and the End User. The Software may not be reverse
  engineered, modified, repackaged, sold, redistributed or otherwise used in a
  way not consistent with the End User License Agreement.
*/
'use strict';

/**
 * Helper class that provides some means of optimizing the rewrite process.
 * Before we build and traverse an AST, we can use this to scan for any
 * tokens we're configured to rewrite and bail early if they aren't in the code.
 * This is especially useful for Protect mode - where we only need to rewrite
 * instances of "eval", which is not likely to occur in most files anyway.
 * As we traverse the AST and make our rewrites, we log which ones occurred.
 * If we end up not rewriting anything (even though we scanned and found tokens),
 * we can still just return the original code unaltered.
 */
module.exports = class RewriteLog {
  constructor(specs, callees, codeString) {
    this.aborted = false;
    this._tokensRewritten = {};
    this._tokenMatches = specs.reduce(
      (matches, spec) =>
        callees[spec.name]
          ? Object.assign(matches, {
              [spec.name]: !spec.token || 0 <= codeString.indexOf(spec.token)
            })
          : matches,
      {}
    );
  }

  /**
   * Documents that there was a rewrite that occurred for
   * the callee of the given name.
   * @param {String} name
   */
  logRewrite(name) {
    /* Please, no pollution. */
    if (!this._tokenMatches[name]) return;
    this._tokensRewritten[name] = true;
  }

  /**
   * Returns, based on the callees provided, whether any of
   * their associated symbols were found in the code string.
   * This gives a quick way to determine, base on our config,
   * if we need to even rewrite the code at all.
   */
  foundTokens() {
    return Object.values(this._tokenMatches).some(Boolean);
  }

  /**
   * During the rewriting process we log which rewrites took
   * place. This returns whether we made any modifications.
   * In reality we might scan the code for tokens and find
   * them, but they might be in comments or do not warrent
   * rewriting e.g. '<a>' + 'Title' + '<a>'. If we did not
   * make changes, we can just return original code.
   */
  rewritesOccurred() {
    return !this.aborted && Object.values(this._tokensRewritten).some(Boolean);
  }
};
