private PrologEnvironment getPrologEnvironment(CurrentUser user) throws RuleEvalException {
   ProjectState projectState = control.getProjectControl().getProjectState();
   PrologEnvironment env;
   try {
     if (rule == null) {
       env = projectState.newPrologEnvironment();
     } else {
       env = projectState.newPrologEnvironment("stdin", new StringReader(rule));
     }
   } catch (CompileException err) {
     String msg;
     if (rule == null && control.getProjectControl().isOwner()) {
       msg = String.format("Cannot load rules.pl for %s: %s", getProjectName(), err.getMessage());
     } else if (rule != null) {
       msg = err.getMessage();
     } else {
       msg = String.format("Cannot load rules.pl for %s", getProjectName());
     }
     throw new RuleEvalException(msg, err);
   }
   env.set(StoredValues.REVIEW_DB, cd.db());
   env.set(StoredValues.CHANGE_DATA, cd);
   env.set(StoredValues.CHANGE_CONTROL, control);
   if (user != null) {
     env.set(StoredValues.CURRENT_USER, user);
   }
   return env;
 }
  private List<Term> evaluateImpl(
      String userRuleLocatorName,
      String userRuleWrapperName,
      String filterRuleLocatorName,
      String filterRuleWrapperName,
      CurrentUser user)
      throws RuleEvalException {
    PrologEnvironment env = getPrologEnvironment(user);
    try {
      Term sr = env.once("gerrit", userRuleLocatorName, new VariableTerm());
      if (fastEvalLabels) {
        env.once("gerrit", "assume_range_from_label");
      }

      List<Term> results = new ArrayList<>();
      try {
        for (Term[] template : env.all("gerrit", userRuleWrapperName, sr, new VariableTerm())) {
          results.add(template[1]);
        }
      } catch (ReductionLimitException err) {
        throw new RuleEvalException(
            String.format(
                "%s on change %d of %s", err.getMessage(), cd.getId().get(), getProjectName()));
      } catch (RuntimeException err) {
        throw new RuleEvalException(
            String.format(
                "Exception calling %s on change %d of %s", sr, cd.getId().get(), getProjectName()),
            err);
      } finally {
        reductionsConsumed = env.getReductions();
      }

      Term resultsTerm = toListTerm(results);
      if (!skipFilters) {
        resultsTerm =
            runSubmitFilters(resultsTerm, env, filterRuleLocatorName, filterRuleWrapperName);
      }
      List<Term> r;
      if (resultsTerm instanceof ListTerm) {
        r = Lists.newArrayList();
        for (Term t = resultsTerm; t instanceof ListTerm; ) {
          ListTerm l = (ListTerm) t;
          r.add(l.car().dereference());
          t = l.cdr().dereference();
        }
      } else {
        r = Collections.emptyList();
      }
      submitRule = sr;
      return r;
    } finally {
      env.close();
    }
  }
  private Term runSubmitFilters(
      Term results,
      PrologEnvironment env,
      String filterRuleLocatorName,
      String filterRuleWrapperName)
      throws RuleEvalException {
    ProjectState projectState = control.getProjectControl().getProjectState();
    PrologEnvironment childEnv = env;
    for (ProjectState parentState : projectState.parents()) {
      PrologEnvironment parentEnv;
      try {
        parentEnv = parentState.newPrologEnvironment();
      } catch (CompileException err) {
        throw new RuleEvalException(
            "Cannot consult rules.pl for " + parentState.getProject().getName(), err);
      }

      parentEnv.copyStoredValues(childEnv);
      Term filterRule = parentEnv.once("gerrit", filterRuleLocatorName, new VariableTerm());
      try {
        if (fastEvalLabels) {
          env.once("gerrit", "assume_range_from_label");
        }

        Term[] template =
            parentEnv.once(
                "gerrit", filterRuleWrapperName, filterRule, results, new VariableTerm());
        results = template[2];
      } catch (ReductionLimitException err) {
        throw new RuleEvalException(
            String.format(
                "%s on change %d of %s",
                err.getMessage(), cd.getId().get(), parentState.getProject().getName()));
      } catch (RuntimeException err) {
        throw new RuleEvalException(
            String.format(
                "Exception calling %s on change %d of %s",
                filterRule, cd.getId().get(), parentState.getProject().getName()),
            err);
      } finally {
        reductionsConsumed += env.getReductions();
      }
      childEnv = parentEnv;
    }
    return results;
  }