gt:         String errorMessage(int offset, String message) {
context: /*
   * Copyright (c) 2009, 2016, Oracle and/or its affiliates. All rights reserved.
   * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   *
   * This code is free software; you can redistribute it and/or modify it
   * under the terms of the GNU General Public License version 2 only, as
   * published by the Free Software Foundation.
   *
   * This code is distributed in the hope that it will be useful, but WITHOUT
   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   * version 2 for more details (a copy is included in the LICENSE file that
   * accompanied this code).
   *
   * You should have received a copy of the GNU General Public License version
   * 2 along with this work; if not, write to the Free Software Foundation,
   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   *
   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   * or visit www.oracle.com if you need additional information or have any
   * questions.
   */
  package org.graalvm.compiler.code;
  
  import java.io.ByteArrayOutputStream;
  import java.io.OutputStream;
  import java.io.PrintStream;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Map;
  import java.util.TreeMap;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  import org.graalvm.compiler.code.CompilationResult.CodeAnnotation;
  import org.graalvm.compiler.code.CompilationResult.CodeComment;
  import org.graalvm.compiler.code.CompilationResult.JumpTable;
  
  import jdk.vm.ci.code.CodeUtil;
  
  /**
   * A HexCodeFile is a textual format for representing a chunk of machine code along with extra
   * information that can be used to enhance a disassembly of the code.
   *
   * A pseudo grammar for a HexCodeFile is given below.
   *
   * <pre>
   *     HexCodeFile ::= Platform Delim HexCode Delim (OptionalSection Delim)*
   *
   *     OptionalSection ::= Comment | OperandComment | JumpTable | LookupTable
   *
   *     Platform ::= "Platform" ISA WordWidth
   *
   *     HexCode ::= "HexCode" StartAddress HexDigits
   *
   *     Comment ::= "Comment" Position String
   *
   *     OperandComment ::= "OperandComment" Position String
   *
   *     JumpTable ::= "JumpTable" Position EntrySize Low High
   *
   *     LookupTable ::= "LookupTable" Position NPairs KeySize OffsetSize
   *
   *     Position, EntrySize, Low, High, NPairs KeySize OffsetSize ::= int
   *
   *     Delim := "&lt;||@"
   * </pre>
   *
   * There must be exactly one HexCode and Platform part in a HexCodeFile. The length of HexDigits
   * must be even as each pair of digits represents a single byte.
   * <p>
   * Below is an example of a valid Code input:
   *
   * <pre>
   *
   *  Platform AMD64 64  &lt;||@
   *  HexCode 0 e8000000009090904883ec084889842410d0ffff48893c24e800000000488b3c24488bf0e8000000004883c408c3  &lt;||@
   *  Comment 24 frame-ref-map: +0 {0}
   *  at java.lang.String.toLowerCase(String.java:2496) [bci: 1]
   *              |0
   *     locals:  |stack:0:a
   *     stack:   |stack:0:a
   *    &lt;||@
   *  OperandComment 24 {java.util.Locale.getDefault()}  &lt;||@
   *  Comment 36 frame-ref-map: +0 {0}
   *  at java.lang.String.toLowerCase(String.java:2496) [bci: 4]
   *              |0
   *     locals:  |stack:0:a
   *    &lt;||@
   *  OperandComment 36 {java.lang.String.toLowerCase(Locale)}  lt;||@
   *
   * </pre>
   */
  public class HexCodeFile {
  
      public static final String NEW_LINE = CodeUtil.NEW_LINE;
      public static final String SECTION_DELIM = " <||@";
      public static final String COLUMN_END = " <|@";
      public static final Pattern SECTION = Pattern.compile("(\\S+)\\s+(.*)", Pattern.DOTALL);
      public static final Pattern COMMENT = Pattern.compile("(\\d+)\\s+(.*)", Pattern.DOTALL);
      public static final Pattern OPERAND_COMMENT = COMMENT;
      public static final Pattern JUMP_TABLE = Pattern.compile("(\\d+)\\s+(\\d+)\\s+(-{0,1}\\d+)\\s+(-{0,1}\\d+)\\s*");
      public static final Pattern LOOKUP_TABLE = Pattern.compile("(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s*");
      public static final Pattern HEX_CODE = Pattern.compile("(\\p{XDigit}+)(?:\\s+(\\p{XDigit}*))?");
      public static final Pattern PLATFORM = Pattern.compile("(\\S+)\\s+(\\S+)", Pattern.DOTALL);
  
      /**
       * Delimiter placed before a HexCodeFile when embedded in a string/stream.
       */
      public static final String EMBEDDED_HCF_OPEN = "<<<HexCodeFile";
  
      /**
       * Delimiter placed after a HexCodeFile when embedded in a string/stream.
       */
      public static final String EMBEDDED_HCF_CLOSE = "HexCodeFile>>>";
  
      /**
       * Map from a machine code position to a list of comments for the position.
       */
      public final Map<Integer, List<String>> comments = new TreeMap<>();
  
      /**
       * Map from a machine code position to a comment for the operands of the instruction at the
       * position.
       */
      public final Map<Integer, List<String>> operandComments = new TreeMap<>();
  
      public final byte[] code;
  
      public final ArrayList<JumpTable> jumpTables = new ArrayList<>();
  
      public final String isa;
  
      public final int wordWidth;
  
      public final long startAddress;
  
      public HexCodeFile(byte[] code, long startAddress, String isa, int wordWidth) {
          this.code = code;
          this.startAddress = startAddress;
          this.isa = isa;
          this.wordWidth = wordWidth;
      }
  
      /**
       * Parses a string in the format produced by {@link #toString()} to produce a
       * {@link HexCodeFile} object.
       */
      public static HexCodeFile parse(String input, int sourceOffset, String source, String sourceName) {
          return new Parser(input, sourceOffset, source, sourceName).hcf;
      }
  
      /**
       * Formats this HexCodeFile as a string that can be parsed with
       * {@link #parse(String, int, String, String)}.
       */
      @Override
      public String toString() {
          ByteArrayOutputStream baos = new ByteArrayOutputStream();
          writeTo(baos);
          return baos.toString();
      }
  
      public String toEmbeddedString() {
          return EMBEDDED_HCF_OPEN + NEW_LINE + toString() + EMBEDDED_HCF_CLOSE;
      }
  
      public void writeTo(OutputStream out) {
          PrintStream ps = out instanceof PrintStream ? (PrintStream) out : new PrintStream(out);
          ps.printf("Platform %s %d %s%n", isa, wordWidth, SECTION_DELIM);
          ps.printf("HexCode %x %s %s%n", startAddress, HexCodeFile.hexCodeString(code), SECTION_DELIM);
  
          for (JumpTable table : jumpTables) {
              ps.printf("JumpTable %d %d %d %d %s%n", table.position, table.entrySize, table.low, table.high, SECTION_DELIM);
          }
  
          for (Map.Entry<Integer, List<String>> e : comments.entrySet()) {
              int pos = e.getKey();
              for (String comment : e.getValue()) {
                  ps.printf("Comment %d %s %s%n", pos, comment, SECTION_DELIM);
              }
          }
  
          for (Map.Entry<Integer, List<String>> e : operandComments.entrySet()) {
              for (String c : e.getValue()) {
                  ps.printf("OperandComment %d %s %s%n", e.getKey(), c, SECTION_DELIM);
              }
          }
          ps.flush();
      }
  
      /**
       * Formats a byte array as a string of hex digits.
       */
      public static String hexCodeString(byte[] code) {
          if (code == null) {
              return "";
          } else {
              StringBuilder sb = new StringBuilder(code.length * 2);
              for (int b : code) {
                  String hex = Integer.toHexString(b & 0xff);
                  if (hex.length() == 1) {
                      sb.append('0');
                  }
                  sb.append(hex);
              }
              return sb.toString();
          }
      }
  
      /**
       * Adds a comment to the list of comments for a given position.
       */
      public void addComment(int pos, String comment) {
          List<String> list = comments.get(pos);
          if (list == null) {
              list = new ArrayList<>();
              comments.put(pos, list);
          }
          list.add(encodeString(comment));
      }
  
      /**
       * Adds an operand comment for a given position.
       */
      public void addOperandComment(int pos, String comment) {
          List<String> list = comments.get(pos);
          if (list == null) {
              list = new ArrayList<>(1);
              comments.put(pos, list);
          }
          list.add(encodeString(comment));
      }
  
      /**
       * Adds any jump tables, lookup tables or code comments from a list of code annotations.
       */
      public static void addAnnotations(HexCodeFile hcf, List<CodeAnnotation> annotations) {
          if (annotations == null || annotations.isEmpty()) {
              return;
          }
          for (CodeAnnotation a : annotations) {
              if (a instanceof JumpTable) {
                  JumpTable table = (JumpTable) a;
                  hcf.jumpTables.add(table);
              } else if (a instanceof CodeComment) {
                  CodeComment comment = (CodeComment) a;
                  hcf.addComment(comment.position, comment.value);
              }
          }
      }
  
      /**
       * Modifies a string to mangle any substrings matching {@link #SECTION_DELIM} and
       * {@link #COLUMN_END}.
       */
      public static String encodeString(String input) {
          int index;
          String s = input;
          while ((index = s.indexOf(SECTION_DELIM)) != -1) {
              s = s.substring(0, index) + " < |@" + s.substring(index + SECTION_DELIM.length());
          }
          while ((index = s.indexOf(COLUMN_END)) != -1) {
              s = s.substring(0, index) + " < @" + s.substring(index + COLUMN_END.length());
          }
          return s;
      }
  
      /**
       * Helper class to parse a string in the format produced by {@link HexCodeFile#toString()} and
       * produce a {@link HexCodeFile} object.
       */
      static class Parser {
  
          final String input;
          final String inputSource;
          String isa;
          int wordWidth;
          byte[] code;
          long startAddress;
          HexCodeFile hcf;
  
          Parser(String input, int sourceOffset, String source, String sourceName) {
              this.input = input;
              this.inputSource = sourceName;
              parseSections(sourceOffset, source);
          }
  
          void makeHCF() {
              if (hcf == null) {
                  if (isa != null && wordWidth != 0 && code != null) {
                      hcf = new HexCodeFile(code, startAddress, isa, wordWidth);
                  }
              }
          }
  
          void checkHCF(String section, int offset) {
              check(hcf != null, offset, section + " section must be after Platform and HexCode section");
          }
  
          void check(boolean condition, int offset, String message) {
              if (!condition) {
                  error(offset, message);
              }
          }
  
          Error error(int offset, String message) {
              throw new Error(errorMessage(offset, message));
          }
  
          void warning(int offset, String message) {
              PrintStream err = System.err;
              err.println("Warning: " + errorMessage(offset, message));
          }
  
  