docs: @VisibleForTesting
    void generateCode(Predicate<String> availableIdentifiers, StringBuilder outputCode) {
  
      outputCode.append(GENERATED_CODE_START_MARKER).append('\n');
  
      // Before entering the real logic, generate any needed prefix.
      generatePrefix(outputCode);
  
      // First we collect all the side tables.
  
      // Like { '\n': '\\n', ... } that map characters to escape.
      List<Map<Character, String>> escapeMaps = Lists.newArrayList();
      // Mangled directive names corresponding to escapeMaps used to generate <namespace>..._ names.
      List<String> escapeMapNames = Lists.newArrayList();
      // Like /[\n\r'"]/g or r'[\n\r\'"]'that match all the characters that need escaping.
      List<String> matchers = Lists.newArrayList();
      // Mangled directive names corresponding to matchers.
      List<String> matcherNames = Lists.newArrayList();
      // RegExps that vet input values.
      List<String> filters = Lists.newArrayList();
      // Mangled directive names corresponding to filters.
      List<String> filterNames = Lists.newArrayList();
      // Bundles of directiveNames and indices into escapeMaps, matchers, etc.
      List<DirectiveDigest> digests = Lists.newArrayList();
  
      escaperLoop:
      for (EscapingConventions.CrossLanguageStringXform escaper :
          EscapingConventions.getAllEscapers()) {
        // "|escapeHtml" -> "escapeHtml"
        String escapeDirectiveIdent = escaper.getDirectiveName().substring(1);
        // "escapeHtml" -> "ESCAPE_HTML"
        String escapeDirectiveUIdent =
            CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, escapeDirectiveIdent);
  
        // If there is an existing function, use it.
        for (String existingFunction : escaper.getLangFunctionNames(getLanguage())) {
          if (availableIdentifiers.test(existingFunction)) {
            useExistingLibraryFunction(outputCode, escapeDirectiveIdent, existingFunction);
            continue escaperLoop;
          }
        }
  
        // Else generate definitions for side tables.
        int escapesVar = -1;
        int matcherVar = -1;
        if (!escaper.getEscapes().isEmpty()) {
          Map<Character, String> escapeMap = Maps.newTreeMap();
          StringBuilder matcherRegexBuf = new StringBuilder(getRegexStart() + "[");
          int lastCodeUnit = Integer.MIN_VALUE;
          int rangeStart = Integer.MIN_VALUE;
          for (EscapingConventions.Escape esc : escaper.getEscapes()) {
            char ch = esc.getPlainText();
            if (ch == lastCodeUnit) {
              throw new IllegalStateException(
                  "Ambiguous escape " + esc.getEscaped() + " for " + escapeDirectiveIdent);
            }
            escapeMap.put(ch, esc.getEscaped());
            if (ch != lastCodeUnit + 1) {
              if (rangeStart != Integer.MIN_VALUE) {
                escapeRegexpRangeOnto((char) rangeStart, (char) lastCodeUnit, matcherRegexBuf);
              }
              rangeStart = ch;
            }
            lastCodeUnit = ch;
          }
          if (rangeStart < 0) {
            throw new IllegalStateException();
          }
          escapeRegexpRangeOnto((char) rangeStart, (char) lastCodeUnit, matcherRegexBuf);
          matcherRegexBuf.append("]").append(getRegexEnd());
  
          // See if we can reuse an existing map.
          int numEscapeMaps = escapeMaps.size();
          for (int i = 0; i < numEscapeMaps; ++i) {
            if (mapsHaveCompatibleOverlap(escapeMaps.get(i), escapeMap)) {
              escapesVar = i;
              break;
            }
          }
          if (escapesVar == -1) {
            escapesVar = numEscapeMaps;
            escapeMaps.add(escapeMap);
            escapeMapNames.add(escapeDirectiveUIdent);
          } else {
            escapeMaps.get(escapesVar).putAll(escapeMap);
            // ESCAPE_JS -> ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX
            escapeMapNames.set(
                escapesVar, escapeMapNames.get(escapesVar) + "__AND__" + escapeDirectiveUIdent);
          }
  
          String matcherRegex = matcherRegexBuf.toString();
          matcherVar = matchers.indexOf(matcherRegex);
          if (matcherVar < 0) {
            matcherVar = matchers.size();
            matchers.add(matcherRegex);
            matcherNames.add(escapeDirectiveUIdent);
          } else {
            matcherNames.set(
                matcherVar, matcherNames.get(matcherVar) + "__AND__" + escapeDirectiveUIdent);
          }
        }
  
        // Find a suitable filter or add one to filters.
        int filterVar = -1;
        Pattern filterPatternJava = escaper.getValueFilter();
        if (filterPatternJava != null) {
          // This is an approximate translation from Java patterns to JavaScript patterns.
          String filterPattern = convertFromJavaRegex(filterPatternJava);
          filterVar = filters.indexOf(filterPattern);
          if (filterVar == -1) {
            filterVar = filters.size();
            filters.add(filterPattern);
            filterNames.add(escapeDirectiveUIdent);
          } else {
            filterNames.set(
                filterVar, filterNames.get(filterVar) + "__AND__" + escapeDirectiveUIdent);
          }
        }
  
        digests.add(
            new DirectiveDigest(
                escapeDirectiveIdent,
                escapesVar,
                matcherVar,
                filterVar,
                escaper.getNonAsciiPrefix(),
                escaper.getInnocuousOutput()));
      }
  
      // TODO(msamuel): Maybe use java Soy templates to generate the JS?
  
      // Output the tables.
      for (int i = 0; i < escapeMaps.size(); ++i) {
        Map<Character, String> escapeMap = escapeMaps.get(i);
        String escapeMapName = escapeMapNames.get(i);
        generateCharacterMapSignature(outputCode, escapeMapName);
        outputCode.append(" = {");
        boolean needsComma = false;
        for (Map.Entry<Character, String> e : escapeMap.entrySet()) {
          if (needsComma) {
            outputCode.append(',');
          }
          outputCode.append("\n  ");
          writeUnsafeStringLiteral(e.getKey(), outputCode);
          outputCode.append(": ");
          writeStringLiteral(e.getValue(), outputCode);
          needsComma = true;
        }
        outputCode.append("\n}").append(getLineEndSyntax()).append("\n");
  
        generateReplacerFunction(outputCode, escapeMapName);
      }
  
      for (int i = 0; i < matchers.size(); ++i) {
        String matcherName = matcherNames.get(i);
        String matcher = matchers.get(i);
        generateMatcher(outputCode, matcherName, matcher);
      }
  
      for (int i = 0; i < filters.size(); ++i) {
        String filterName = filterNames.get(i);
        String filter = filters.get(i);
        generateFilter(outputCode, filterName, filter);
      }
  
      // Finally, define the helper functions that use the escapes, filters, matchers, etc.
      for (DirectiveDigest digest : digests) {
        digest.updateNames(escapeMapNames, matcherNames, filterNames);
        generateHelperFunction(outputCode, digest);
      }
  
      // Emit patterns and constants needed by escaping functions that are not part of any one
      // escaping convention.
      generateCommonConstants(outputCode);
  
      outputCode.append('\n').append(GENERATED_CODE_END_MARKER).append('\n');
    }