# HG changeset patch # User Christian Wimmer # Date 1352508909 28800 # Node ID 75785a8ba5a3bad4309bbe186fb63a0515fcb9d9 # Parent 349127dc59dede47412cb196edbc65a849a5d9c2 HexCodeFile is not HotSpot-specific, so move it to printer project diff -r 349127dc59de -r 75785a8ba5a3 graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HexCodeFile.java --- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HexCodeFile.java Fri Nov 09 16:11:12 2012 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,434 +0,0 @@ -/* - * Copyright (c) 2009, 2011, 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 com.oracle.graal.hotspot; - -import java.io.*; -import java.util.*; -import java.util.regex.*; - -import com.oracle.graal.api.code.*; -import com.oracle.graal.api.code.CompilationResult.CodeAnnotation; -import com.oracle.graal.api.code.CompilationResult.CodeComment; -import com.oracle.graal.api.code.CompilationResult.JumpTable; -import com.oracle.graal.api.code.CompilationResult.LookupTable; - - -/** - * 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. - *
- *     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 := "<||@"
- * 
- * - * 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. - *

- * Below is an example of a valid Code input: - *

- *
- *  Platform AMD64 64  <||@
- *  HexCode 0 e8000000009090904883ec084889842410d0ffff48893c24e800000000488b3c24488bf0e8000000004883c408c3  <||@
- *  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
- *    <||@
- *  OperandComment 24 {java.util.Locale.getDefault()}  <||@
- *  Comment 36 frame-ref-map: +0 {0}
- *  at java.lang.String.toLowerCase(String.java:2496) [bci: 4]
- *              |0
- *     locals:  |stack:0:a
- *    <||@
- *  OperandComment 36 {java.lang.String.toLowerCase(Locale)}  <||@
- *
- * 
- */ -public class HexCodeFile { - - public static final String NEW_LINE = CodeUtil.NEW_LINE; - public static final String SECTION_DELIM = " <||@"; - 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 = "<<> comments = new TreeMap<>(); - - /** - * Map from a machine code position to a comment for the operands of the instruction at the position. - */ - public final Map operandComments = new TreeMap<>(); - - public final byte[] code; - - public final ArrayList jumpTables = new ArrayList<>(); - - public final ArrayList lookupTables = 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 (LookupTable table : lookupTables) { - ps.printf("LookupTable %d %d %d %d %s%n", table.position, table.npairs, table.keySize, table.keySize, SECTION_DELIM); - } - - for (Map.Entry> 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 e : operandComments.entrySet()) { - ps.printf("OperandComment %d %s %s%n", e.getKey(), e.getValue(), SECTION_DELIM); - } - ps.flush(); - } - - - /** - * Formats a byte array as a string of hex digits. - */ - public static String hexCodeString(byte[] code) { - 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 list = comments.get(pos); - if (list == null) { - list = new ArrayList<>(); - comments.put(pos, list); - } - list.add(encodeString(comment)); - } - - /** - * Sets an operand comment for a given position. - * - * @return the previous operand comment for {@code pos} - */ - public String addOperandComment(int pos, String comment) { - return operandComments.put(pos, encodeString(comment)); - } - - /** - * Adds any jump tables, lookup tables or code comments from a list of code annotations. - */ - public static void addAnnotations(HexCodeFile hcf, List 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 LookupTable) { - LookupTable table = (LookupTable) a; - hcf.lookupTables.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}. - */ - 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()); - } - 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) { - System.err.println("Warning: " + errorMessage(offset, message)); - } - - String errorMessage(int offset, String message) { - assert offset < input.length(); - InputPos inputPos = filePos(offset); - int lineEnd = input.indexOf(HexCodeFile.NEW_LINE, offset); - int lineStart = offset - inputPos.col; - String line = lineEnd == -1 ? input.substring(lineStart) : input.substring(lineStart, lineEnd); - return String.format("%s:%d: %s%n%s%n%" + (inputPos.col + 1) + "s", inputSource, inputPos.line, message, line, "^"); - } - - static class InputPos { - final int line; - final int col; - public InputPos(int line, int col) { - this.line = line; - this.col = col; - } - } - - InputPos filePos(int index) { - assert input != null; - int lineStart = input.lastIndexOf(HexCodeFile.NEW_LINE, index) + 1; - - String l = input.substring(lineStart, lineStart + 10); - System.out.println("YYY" + input.substring(index, index + 10) + "..."); - System.out.println("XXX" + l + "..."); - - int pos = input.indexOf(HexCodeFile.NEW_LINE, 0); - int line = 1; - while (pos > 0 && pos < index) { - line++; - pos = input.indexOf(HexCodeFile.NEW_LINE, pos + 1); - } - return new InputPos(line, index - lineStart); - } - - void parseSections(int offset, String source) { - assert input.startsWith(source, offset); - int index = 0; - int endIndex = source.indexOf(SECTION_DELIM); - while (endIndex != -1) { - while (source.charAt(index) <= ' ') { - index++; - } - String section = source.substring(index, endIndex).trim(); - parseSection(offset + index, section); - index = endIndex + SECTION_DELIM.length(); - endIndex = source.indexOf(SECTION_DELIM, index); - } - } - - int parseInt(int offset, String value) { - try { - return Integer.parseInt(value); - } catch (NumberFormatException e) { - throw error(offset, "Not a valid integer: " + value); - } - } - - void parseSection(int offset, String section) { - if (section.isEmpty()) { - return; - } - assert input.startsWith(section, offset); - Matcher m = HexCodeFile.SECTION.matcher(section); - check(m.matches(), offset, "Section does not match pattern " + HexCodeFile.SECTION); - - String header = m.group(1); - String body = m.group(2); - int headerOffset = offset + m.start(1); - int bodyOffset = offset + m.start(2); - - if (header.equals("Platform")) { - check(isa == null, bodyOffset, "Duplicate Platform section found"); - m = HexCodeFile.PLATFORM.matcher(body); - check(m.matches(), bodyOffset, "Platform does not match pattern " + HexCodeFile.PLATFORM); - isa = m.group(1); - wordWidth = parseInt(bodyOffset + m.start(2), m.group(2)); - makeHCF(); - } else if (header.equals("HexCode")) { - check(code == null, bodyOffset, "Duplicate Code section found"); - m = HexCodeFile.HEX_CODE.matcher(body); - check(m.matches(), bodyOffset, "Code does not match pattern " + HexCodeFile.HEX_CODE); - String hexAddress = m.group(1); - startAddress = Long.valueOf(hexAddress, 16); - String hexCode = m.group(2); - if (hexCode == null) { - code = new byte[0]; - } else { - check((hexCode.length() % 2) == 0, bodyOffset, "Hex code length must be even"); - code = new byte[hexCode.length() / 2]; - for (int i = 0; i < code.length; i++) { - String hexByte = hexCode.substring(i * 2, (i + 1) * 2); - code[i] = (byte) Integer.parseInt(hexByte, 16); - } - } - makeHCF(); - } else if (header.equals("Comment")) { - checkHCF("Comment", headerOffset); - m = HexCodeFile.COMMENT.matcher(body); - check(m.matches(), bodyOffset, "Comment does not match pattern " + HexCodeFile.COMMENT); - int pos = parseInt(bodyOffset + m.start(1), m.group(1)); - String comment = m.group(2); - hcf.addComment(pos, comment); - } else if (header.equals("OperandComment")) { - checkHCF("OperandComment", headerOffset); - m = HexCodeFile.OPERAND_COMMENT.matcher(body); - check(m.matches(), bodyOffset, "OperandComment does not match pattern " + HexCodeFile.OPERAND_COMMENT); - int pos = parseInt(bodyOffset + m.start(1), m.group(1)); - String comment = m.group(2); - hcf.addOperandComment(pos, comment); - } else if (header.equals("JumpTable")) { - checkHCF("JumpTable", headerOffset); - m = HexCodeFile.JUMP_TABLE.matcher(body); - check(m.matches(), bodyOffset, "JumpTable does not match pattern " + HexCodeFile.JUMP_TABLE); - int pos = parseInt(bodyOffset + m.start(1), m.group(1)); - int entrySize = parseInt(bodyOffset + m.start(2), m.group(2)); - int low = parseInt(bodyOffset + m.start(3), m.group(3)); - int high = parseInt(bodyOffset + m.start(4), m.group(4)); - hcf.jumpTables.add(new JumpTable(pos, low, high, entrySize)); - } else if (header.equals("LookupTable")) { - checkHCF("LookupTable", headerOffset); - m = HexCodeFile.LOOKUP_TABLE.matcher(body); - check(m.matches(), bodyOffset, "LookupTable does not match pattern " + HexCodeFile.LOOKUP_TABLE); - int pos = parseInt(bodyOffset + m.start(1), m.group(1)); - int npairs = parseInt(bodyOffset + m.start(2), m.group(2)); - int keySize = parseInt(bodyOffset + m.start(3), m.group(3)); - int offsetSize = parseInt(bodyOffset + m.start(4), m.group(4)); - hcf.lookupTables.add(new LookupTable(pos, npairs, keySize, offsetSize)); - } else { - error(offset, "Unknown section header: " + header); - } - } - } -} diff -r 349127dc59de -r 75785a8ba5a3 graal/com.oracle.graal.printer/src/com/oracle/graal/printer/HexCodeFile.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/HexCodeFile.java Fri Nov 09 16:55:09 2012 -0800 @@ -0,0 +1,434 @@ +/* + * Copyright (c) 2009, 2011, 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 com.oracle.graal.printer; + +import java.io.*; +import java.util.*; +import java.util.regex.*; + +import com.oracle.graal.api.code.*; +import com.oracle.graal.api.code.CompilationResult.CodeAnnotation; +import com.oracle.graal.api.code.CompilationResult.CodeComment; +import com.oracle.graal.api.code.CompilationResult.JumpTable; +import com.oracle.graal.api.code.CompilationResult.LookupTable; + + +/** + * 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. + *
+ *     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 := "<||@"
+ * 
+ * + * 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. + *

+ * Below is an example of a valid Code input: + *

+ *
+ *  Platform AMD64 64  <||@
+ *  HexCode 0 e8000000009090904883ec084889842410d0ffff48893c24e800000000488b3c24488bf0e8000000004883c408c3  <||@
+ *  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
+ *    <||@
+ *  OperandComment 24 {java.util.Locale.getDefault()}  <||@
+ *  Comment 36 frame-ref-map: +0 {0}
+ *  at java.lang.String.toLowerCase(String.java:2496) [bci: 4]
+ *              |0
+ *     locals:  |stack:0:a
+ *    <||@
+ *  OperandComment 36 {java.lang.String.toLowerCase(Locale)}  <||@
+ *
+ * 
+ */ +public class HexCodeFile { + + public static final String NEW_LINE = CodeUtil.NEW_LINE; + public static final String SECTION_DELIM = " <||@"; + 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 = "<<> comments = new TreeMap<>(); + + /** + * Map from a machine code position to a comment for the operands of the instruction at the position. + */ + public final Map operandComments = new TreeMap<>(); + + public final byte[] code; + + public final ArrayList jumpTables = new ArrayList<>(); + + public final ArrayList lookupTables = 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 (LookupTable table : lookupTables) { + ps.printf("LookupTable %d %d %d %d %s%n", table.position, table.npairs, table.keySize, table.keySize, SECTION_DELIM); + } + + for (Map.Entry> 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 e : operandComments.entrySet()) { + ps.printf("OperandComment %d %s %s%n", e.getKey(), e.getValue(), SECTION_DELIM); + } + ps.flush(); + } + + + /** + * Formats a byte array as a string of hex digits. + */ + public static String hexCodeString(byte[] code) { + 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 list = comments.get(pos); + if (list == null) { + list = new ArrayList<>(); + comments.put(pos, list); + } + list.add(encodeString(comment)); + } + + /** + * Sets an operand comment for a given position. + * + * @return the previous operand comment for {@code pos} + */ + public String addOperandComment(int pos, String comment) { + return operandComments.put(pos, encodeString(comment)); + } + + /** + * Adds any jump tables, lookup tables or code comments from a list of code annotations. + */ + public static void addAnnotations(HexCodeFile hcf, List 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 LookupTable) { + LookupTable table = (LookupTable) a; + hcf.lookupTables.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}. + */ + 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()); + } + 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) { + System.err.println("Warning: " + errorMessage(offset, message)); + } + + String errorMessage(int offset, String message) { + assert offset < input.length(); + InputPos inputPos = filePos(offset); + int lineEnd = input.indexOf(HexCodeFile.NEW_LINE, offset); + int lineStart = offset - inputPos.col; + String line = lineEnd == -1 ? input.substring(lineStart) : input.substring(lineStart, lineEnd); + return String.format("%s:%d: %s%n%s%n%" + (inputPos.col + 1) + "s", inputSource, inputPos.line, message, line, "^"); + } + + static class InputPos { + final int line; + final int col; + public InputPos(int line, int col) { + this.line = line; + this.col = col; + } + } + + InputPos filePos(int index) { + assert input != null; + int lineStart = input.lastIndexOf(HexCodeFile.NEW_LINE, index) + 1; + + String l = input.substring(lineStart, lineStart + 10); + System.out.println("YYY" + input.substring(index, index + 10) + "..."); + System.out.println("XXX" + l + "..."); + + int pos = input.indexOf(HexCodeFile.NEW_LINE, 0); + int line = 1; + while (pos > 0 && pos < index) { + line++; + pos = input.indexOf(HexCodeFile.NEW_LINE, pos + 1); + } + return new InputPos(line, index - lineStart); + } + + void parseSections(int offset, String source) { + assert input.startsWith(source, offset); + int index = 0; + int endIndex = source.indexOf(SECTION_DELIM); + while (endIndex != -1) { + while (source.charAt(index) <= ' ') { + index++; + } + String section = source.substring(index, endIndex).trim(); + parseSection(offset + index, section); + index = endIndex + SECTION_DELIM.length(); + endIndex = source.indexOf(SECTION_DELIM, index); + } + } + + int parseInt(int offset, String value) { + try { + return Integer.parseInt(value); + } catch (NumberFormatException e) { + throw error(offset, "Not a valid integer: " + value); + } + } + + void parseSection(int offset, String section) { + if (section.isEmpty()) { + return; + } + assert input.startsWith(section, offset); + Matcher m = HexCodeFile.SECTION.matcher(section); + check(m.matches(), offset, "Section does not match pattern " + HexCodeFile.SECTION); + + String header = m.group(1); + String body = m.group(2); + int headerOffset = offset + m.start(1); + int bodyOffset = offset + m.start(2); + + if (header.equals("Platform")) { + check(isa == null, bodyOffset, "Duplicate Platform section found"); + m = HexCodeFile.PLATFORM.matcher(body); + check(m.matches(), bodyOffset, "Platform does not match pattern " + HexCodeFile.PLATFORM); + isa = m.group(1); + wordWidth = parseInt(bodyOffset + m.start(2), m.group(2)); + makeHCF(); + } else if (header.equals("HexCode")) { + check(code == null, bodyOffset, "Duplicate Code section found"); + m = HexCodeFile.HEX_CODE.matcher(body); + check(m.matches(), bodyOffset, "Code does not match pattern " + HexCodeFile.HEX_CODE); + String hexAddress = m.group(1); + startAddress = Long.valueOf(hexAddress, 16); + String hexCode = m.group(2); + if (hexCode == null) { + code = new byte[0]; + } else { + check((hexCode.length() % 2) == 0, bodyOffset, "Hex code length must be even"); + code = new byte[hexCode.length() / 2]; + for (int i = 0; i < code.length; i++) { + String hexByte = hexCode.substring(i * 2, (i + 1) * 2); + code[i] = (byte) Integer.parseInt(hexByte, 16); + } + } + makeHCF(); + } else if (header.equals("Comment")) { + checkHCF("Comment", headerOffset); + m = HexCodeFile.COMMENT.matcher(body); + check(m.matches(), bodyOffset, "Comment does not match pattern " + HexCodeFile.COMMENT); + int pos = parseInt(bodyOffset + m.start(1), m.group(1)); + String comment = m.group(2); + hcf.addComment(pos, comment); + } else if (header.equals("OperandComment")) { + checkHCF("OperandComment", headerOffset); + m = HexCodeFile.OPERAND_COMMENT.matcher(body); + check(m.matches(), bodyOffset, "OperandComment does not match pattern " + HexCodeFile.OPERAND_COMMENT); + int pos = parseInt(bodyOffset + m.start(1), m.group(1)); + String comment = m.group(2); + hcf.addOperandComment(pos, comment); + } else if (header.equals("JumpTable")) { + checkHCF("JumpTable", headerOffset); + m = HexCodeFile.JUMP_TABLE.matcher(body); + check(m.matches(), bodyOffset, "JumpTable does not match pattern " + HexCodeFile.JUMP_TABLE); + int pos = parseInt(bodyOffset + m.start(1), m.group(1)); + int entrySize = parseInt(bodyOffset + m.start(2), m.group(2)); + int low = parseInt(bodyOffset + m.start(3), m.group(3)); + int high = parseInt(bodyOffset + m.start(4), m.group(4)); + hcf.jumpTables.add(new JumpTable(pos, low, high, entrySize)); + } else if (header.equals("LookupTable")) { + checkHCF("LookupTable", headerOffset); + m = HexCodeFile.LOOKUP_TABLE.matcher(body); + check(m.matches(), bodyOffset, "LookupTable does not match pattern " + HexCodeFile.LOOKUP_TABLE); + int pos = parseInt(bodyOffset + m.start(1), m.group(1)); + int npairs = parseInt(bodyOffset + m.start(2), m.group(2)); + int keySize = parseInt(bodyOffset + m.start(3), m.group(3)); + int offsetSize = parseInt(bodyOffset + m.start(4), m.group(4)); + hcf.lookupTables.add(new LookupTable(pos, npairs, keySize, offsetSize)); + } else { + error(offset, "Unknown section header: " + header); + } + } + } +}