"use strict";

const EVAL_SCOPE_MARKER = Symbol("evalInScope");

module.exports = {
  EVAL_SCOPE_MARKER,
  getEvalScopes,
  markEvalScopes,
  isMarked,
  hasEval,
};

function getEvalScopes(path) {
  const evalScopes = new Set;

  function add(scope) {
    let evalScope = scope;
    do {
      evalScopes.add(evalScope);
    } while (evalScope = evalScope.parent);
  }

  path.traverse({
    CallExpression(evalPath) {
      const callee = evalPath.get("callee");

      if (callee.isIdentifier() && callee.node.name === "eval" && !callee.scope.getBinding("eval")) {
        add(callee.scope);
      }
    }
  });

  return evalScopes;
}

function markEvalScopes(path, key = EVAL_SCOPE_MARKER) {
  const evalScopes = getEvalScopes(path);
  [...evalScopes].forEach((scope) => {
    scope[key] = true;
  });
}

function isMarked(scope, key = EVAL_SCOPE_MARKER) {
  return Object.prototype.hasOwnProperty.call(scope, key);
}

function hasEval(scope, key = EVAL_SCOPE_MARKER) {
  if (!isMarked(scope, key)) {
    markEvalScopes(scope, key);
  }
  return scope[key];
}
