From 34d6be2ac4cc085f73e0e83ca95bf9ddc4631f9c Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Fri, 8 Sep 2023 17:15:11 +0100 Subject: [PATCH] Change Print to use a StringBuilder We normally want to write to a String, not directly to stdout, so this it a little easier. --- src/main/java/org/squiddev/cobalt/Print.java | 245 ++++++++---------- .../cobalt/function/LuaInterpreter.java | 11 +- .../cobalt/compiler/CompilerUnitTests.java | 11 +- 3 files changed, 115 insertions(+), 152 deletions(-) diff --git a/src/main/java/org/squiddev/cobalt/Print.java b/src/main/java/org/squiddev/cobalt/Print.java index 6f5a2b15..7a0b7d8e 100644 --- a/src/main/java/org/squiddev/cobalt/Print.java +++ b/src/main/java/org/squiddev/cobalt/Print.java @@ -26,20 +26,16 @@ import org.squiddev.cobalt.function.LuaClosure; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.util.function.Consumer; - import static org.squiddev.cobalt.Lua.*; /** - * Debug helper class to pretty-print lua bytecodes. + * Debug helper class for pretty-printing Lua bytecode * * @see Prototype * @see LuaClosure */ -public class Print { - public static final String[] OPNAMES = { +public final class Print { + private static final String[] OPNAMES = { "MOVE", "LOADK", "LOADBOOL", @@ -81,70 +77,69 @@ public class Print { null, }; - - static void printString(PrintWriter ps, final LuaString s) { - - ps.print('"'); + private static void printString(StringBuilder out, final LuaString s) { + out.append('"'); for (int i = 0, n = s.length(); i < n; i++) { int c = s.charAt(i); if (c >= ' ' && c <= '~' && c != '\"' && c != '\\') { - ps.print((char) c); + out.append((char) c); } else { switch (c) { - case '"' -> ps.print("\\\""); - case '\\' -> ps.print("\\\\"); - case 0x0007 -> ps.print("\\a"); - case '\b' -> ps.print("\\b"); - case '\f' -> ps.print("\\f"); - case '\t' -> ps.print("\\t"); - case '\r' -> ps.print("\\r"); - case '\n' -> ps.print("\\n"); - case 0x000B -> ps.print("\\v"); + case '"' -> out.append("\\\""); + case '\\' -> out.append("\\\\"); + case 0x0007 -> out.append("\\a"); + case '\b' -> out.append("\\b"); + case '\f' -> out.append("\\f"); + case '\t' -> out.append("\\t"); + case '\r' -> out.append("\\r"); + case '\n' -> out.append("\\n"); + case 0x000B -> out.append("\\v"); default -> { - ps.print('\\'); - ps.print(Integer.toString(1000 + c).substring(1)); + out.append('\\'); + out.append(Integer.toString(1000 + c).substring(1)); } } } } - ps.print('"'); + out.append('"'); } - private static void printValue(PrintWriter ps, LuaValue v) { + private static void printValue(StringBuilder out, LuaValue v) { switch (v.type()) { - case Constants.TSTRING -> printString(ps, (LuaString) v); - default -> ps.print(v); + case Constants.TSTRING -> printString(out, (LuaString) v); + default -> out.append(v); } } - private static void printConstant(PrintWriter ps, Prototype f, int i) { - printValue(ps, f.constants[i]); + private static void printConstant(StringBuilder out, Prototype f, int i) { + printValue(out, f.constants[i]); } /** * Print the code in a prototype * + * @param out The buffer to write to. * @param f the {@link Prototype} * @param extended Included extended/non-standard information. */ - public static void printCode(PrintWriter ps, Prototype f, boolean extended) { + public static void printCode(StringBuilder out, Prototype f, boolean extended) { int[] code = f.code; int pc, n = code.length; for (pc = 0; pc < n; pc++) { - printOpcode(ps, f, pc, extended); - ps.println(); + printOpcode(out, f, pc, extended); + out.append("\n"); } } /** * Print an opcode in a prototype * - * @param ps the {@link PrintWriter} to print to - * @param f the {@link Prototype} - * @param pc the program counter to look up and print + * @param out The {@link StringBuilder} to write to. + * @param f The {@link Prototype} + * @param pc The program counter to look up and print. * @param extended Included extended/non-standard information. */ - public static void printOpcode(PrintWriter ps, Prototype f, int pc, boolean extended) { + public static void printOpcode(StringBuilder out, Prototype f, int pc, boolean extended) { int[] code = f.code; int i = code[pc]; int o = GET_OPCODE(i); @@ -153,97 +148,90 @@ public static void printOpcode(PrintWriter ps, Prototype f, int pc, boolean exte int c = GETARG_C(i); int bx = GETARG_Bx(i); int sbx = GETARG_sBx(i); + + out.append(" ").append(pc + 1).append(" "); + int line = f.lineAt(pc); int column = f.columnAt(pc); - ps.print(" " + (pc + 1) + " "); if (extended && line > 0 && column > 0) { - ps.print("[" + line + "/" + column + "] "); + out.append("[").append(line).append("/").append(column).append("] "); } else if (line > 0) { - ps.print("[" + line + "] "); + out.append("[").append(line).append("] "); } else { - ps.print("[-] "); + out.append("[-] "); } - ps.print(OPNAMES[o] + " "); + + out.append(OPNAMES[o]).append(" "); switch (getOpMode(o)) { case iABC -> { - ps.print(a); - if (getBMode(o) != OpArgN) { - ps.print(" " + (ISK(b) ? (-1 - INDEXK(b)) : b)); - } - if (getCMode(o) != OpArgN) { - ps.print(" " + (ISK(c) ? (-1 - INDEXK(c)) : c)); - } - } - case iABx -> { - if (getBMode(o) == OpArgK) { - ps.print(a + " " + (-1 - bx)); - } else { - ps.print(a + " " + (bx)); - } + out.append(a); + if (getBMode(o) != OpArgN) out.append(" ").append(ISK(b) ? (-1 - INDEXK(b)) : b); + if (getCMode(o) != OpArgN) out.append(" ").append(ISK(c) ? (-1 - INDEXK(c)) : c); } + case iABx -> out.append(a).append(" ").append(getBMode(o) == OpArgK ? -1 - bx : bx); case iAsBx -> { if (o == OP_JMP) { - ps.print(sbx); + out.append(sbx); } else { - ps.print(a + " " + sbx); + out.append(a).append(" ").append(sbx); } } } switch (o) { case OP_LOADK -> { - ps.print(" ; "); - printConstant(ps, f, bx); + out.append(" ; "); + printConstant(out, f, bx); } case OP_GETUPVAL, OP_SETUPVAL -> { - ps.print(" ; "); + out.append(" ; "); if (f.upvalueNames.length > b) { - printValue(ps, f.upvalueNames[b]); + printValue(out, f.upvalueNames[b]); } else { - ps.print("-"); + out.append("-"); } } case OP_GETGLOBAL, OP_SETGLOBAL -> { - ps.print(" ; "); - printConstant(ps, f, bx); + out.append(" ; "); + printConstant(out, f, bx); } case OP_GETTABLE, OP_SELF -> { if (ISK(c)) { - ps.print(" ; "); - printConstant(ps, f, INDEXK(c)); + out.append(" ; "); + printConstant(out, f, INDEXK(c)); } } case OP_SETTABLE, OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_POW, OP_EQ, OP_LT, OP_LE -> { if (ISK(b) || ISK(c)) { - ps.print(" ; "); + out.append(" ; "); if (ISK(b)) { - printConstant(ps, f, INDEXK(b)); + printConstant(out, f, INDEXK(b)); } else { - ps.print("-"); + out.append("-"); } - ps.print(" "); + out.append(" "); if (ISK(c)) { - printConstant(ps, f, INDEXK(c)); + printConstant(out, f, INDEXK(c)); } else { - ps.print("-"); + out.append("-"); } } } - case OP_JMP, OP_FORLOOP, OP_FORPREP -> ps.print(" ; to " + (sbx + pc + 2)); - case OP_CLOSURE -> ps.print(" ; " + f.children[bx].getClass().getName()); + case OP_JMP, OP_FORLOOP, OP_FORPREP -> out.append(" ; to ").append(sbx + pc + 2); + case OP_CLOSURE -> out.append(" ; ").append(f.children[bx].getClass().getName()); case OP_SETLIST -> { if (c == 0) { - ps.print(" ; " + code[++pc]); + out.append(" ; ").append(code[++pc]); } else { - ps.print(" ; " + c); + out.append(" ; ").append(c); } } - case OP_VARARG -> ps.print(" ; is_vararg=" + f.isVarArg); + case OP_VARARG -> out.append(" ; is_vararg=").append(f.isVarArg); default -> { } } } - private static void printHeader(PrintWriter ps, Prototype f) { + private static void printHeader(StringBuilder out, Prototype f) { String s = String.valueOf(f.source); if (s.startsWith("@") || s.startsWith("=")) { s = s.substring(1); @@ -253,64 +241,50 @@ private static void printHeader(PrintWriter ps, Prototype f) { s = "(string)"; } String a = (f.lineDefined == 0) ? "main" : "function"; - ps.print("\n%" + a + " <" + s + ":" + f.lineDefined + "," - + f.lastLineDefined + "> (" + f.code.length + " instructions, " - + f.code.length * 4 + " bytes at " + id(f) + ")\n"); - ps.print(f.parameters + " param, " + f.maxStackSize + " slot, " - + f.upvalueNames.length + " upvalue, "); - ps.print(f.locals.length + " local, " + f.constants.length - + " constant, " + f.children.length + " function\n"); + out.append("\n%").append(a) + .append(" <").append(s).append(":").append(f.lineDefined).append(",").append(f.lastLineDefined).append("> (") + .append(f.code.length).append(" instructions, ").append(f.code.length * 4).append(" bytes at ").append(id(f)).append(")\n"); + out.append(f.parameters).append(" param, ").append(f.maxStackSize).append(" slot, ").append(f.upvalueNames.length).append(" upvalue, "); + out.append(f.locals.length).append(" local, ").append(f.constants.length).append(" constant, ").append(f.children.length).append(" function\n"); } - private static void printConstants(PrintWriter ps, Prototype f) { + private static void printConstants(StringBuilder out, Prototype f) { int i, n = f.constants.length; - ps.print("constants (" + n + ") for " + id(f) + ":\n"); + out.append("constants (").append(n).append(") for ").append(id(f)).append(":\n"); for (i = 0; i < n; i++) { - ps.print(" " + (i + 1) + " "); - printValue(ps, f.constants[i]); - ps.print("\n"); + out.append(" ").append(i + 1).append(" "); + printValue(out, f.constants[i]); + out.append("\n"); } } - private static void printLocals(PrintWriter ps, Prototype f) { + private static void printLocals(StringBuilder out, Prototype f) { int i, n = f.locals.length; - ps.print("locals (" + n + ") for " + id(f) + ":\n"); + out.append("locals (").append(n).append(") for ").append(id(f)).append(":\n"); for (i = 0; i < n; i++) { - ps.println(" " + i + " " + f.locals[i].name + " " + (f.locals[i].startpc + 1) + " " + (f.locals[i].endpc + 1)); + out.append(" ").append(i).append(" ").append(f.locals[i].name).append(" ").append(f.locals[i].startpc + 1).append(" ").append(f.locals[i].endpc + 1).append("\n"); } } - private static void printUpValues(PrintWriter ps, Prototype f) { + private static void printUpValues(StringBuilder out, Prototype f) { int i, n = f.upvalueNames.length; - ps.print("upvalues (" + n + ") for " + id(f) + ":\n"); + out.append("upvalues (").append(n).append(") for ").append(id(f)).append(":\n"); for (i = 0; i < n; i++) { - ps.print(" " + i + " " + f.upvalueNames[i] + "\n"); + out.append(" ").append(i).append(" ").append(f.upvalueNames[i]).append("\n"); } } - public static void printFunction(PrintWriter ps, Prototype f, boolean full, boolean extended) { + public static void printFunction(StringBuilder out, Prototype f, boolean full, boolean extended) { int i, n = f.children.length; - printHeader(ps, f); - printCode(ps, f, extended); + printHeader(out, f); + printCode(out, f, extended); if (full) { - printConstants(ps, f); - printLocals(ps, f); - printUpValues(ps, f); + printConstants(out, f); + printLocals(out, f); + printUpValues(out, f); } for (i = 0; i < n; i++) { - printFunction(ps, f.children[i], full, extended); - } - } - - private static void format(PrintWriter ps, String s, int maxcols) { - int n = s.length(); - if (n > maxcols) { - ps.print(s.substring(0, maxcols)); - } else { - ps.print(s); - for (int i = maxcols - n; --i >= 0; ) { - ps.print(' '); - } + printFunction(out, f.children[i], full, extended); } } @@ -321,52 +295,45 @@ private static String id(Prototype f) { /** * Print the state of a {@link LuaClosure} that is being executed * - * @param cl the {@link LuaClosure} - * @param pc the program counter - * @param stack the stack of {@link LuaValue} - * @param top the top of the stack + * @param out The string builder to write to. + * @param cl The {@link LuaClosure} + * @param pc The program counter + * @param stack The stack of {@link LuaValue} + * @param top The top of the stack * @param varargs any {@link Varargs} value that may apply */ - public static void printState(PrintWriter ps, LuaClosure cl, int pc, LuaValue[] stack, int top, Varargs varargs) { + public static void printState(StringBuilder out, LuaClosure cl, int pc, LuaValue[] stack, int top) { // print opcode into buffer - format(ps, showWith(p -> printOpcode(p, cl.getPrototype(), pc, true)), 50); + int len = out.length(); + printOpcode(out, cl.getPrototype(), pc, true); + while (out.length() < len + 50) out.append(' '); // print stack - ps.print('['); + out.append('['); for (int i = 0; i < stack.length; i++) { LuaValue v = stack[i]; if (v == null) { - ps.print("null"); + out.append("null"); } else { switch (v.type()) { case Constants.TSTRING -> { LuaString s = (LuaString) v; - ps.print(s.length() < 48 ? + out.append(s.length() < 48 ? s.toString() : s.substringOfEnd(0, 32) + "...+" + (s.length() - 32) + "b"); } - case Constants.TFUNCTION -> ps.print((v instanceof LuaClosure) ? + case Constants.TFUNCTION -> out.append((v instanceof LuaClosure) ? ((LuaClosure) v).getPrototype().toString() : v.toString()); case Constants.TUSERDATA -> { - ps.print(v); + out.append(v); } - default -> ps.print(v); + default -> out.append(v); } } if (i + 1 == top) { - ps.print(']'); + out.append(']'); } - ps.print(" | "); - } - ps.print(varargs); - ps.println(); - } - - private static String showWith(Consumer f) { - StringWriter output = new StringWriter(); - try (PrintWriter ps = new PrintWriter(output)) { - f.accept(ps); + out.append(" | "); } - return output.toString(); } } diff --git a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java index 64c6ce76..103e20d1 100644 --- a/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java +++ b/src/main/java/org/squiddev/cobalt/function/LuaInterpreter.java @@ -30,9 +30,6 @@ import org.squiddev.cobalt.debug.Upvalue; import org.squiddev.cobalt.lib.TableLib; -import java.io.PrintWriter; -import java.io.StringWriter; - import static org.squiddev.cobalt.Constants.*; import static org.squiddev.cobalt.Lua.*; import static org.squiddev.cobalt.LuaDouble.valueOf; @@ -811,11 +808,9 @@ public static Varargs resumeReturn(LuaState state, DebugState ds, DebugFrame di, private static LuaError reportIllegalResume(LuaState state, Prototype prototype, int pc) { LuaError err = new LuaError("cannot resume this opcode"); state.reportInternalError(err, () -> { - StringWriter output = new StringWriter(); - try (PrintWriter ps = new PrintWriter(output)) { - ps.printf("Resuming function at invalid opcode. file=\"%s\", pc=%d\n", prototype.shortSource(), pc + 1); - Print.printCode(ps, prototype, true); - } + StringBuilder output = new StringBuilder(); + output.append(String.format("Resuming function at invalid opcode. file=\"%s\", pc=%d\n", prototype.shortSource(), pc + 1)); + Print.printCode(output, prototype, true); return output.toString(); }); return err; diff --git a/src/test/java/org/squiddev/cobalt/compiler/CompilerUnitTests.java b/src/test/java/org/squiddev/cobalt/compiler/CompilerUnitTests.java index 73ab0ddd..60f7dfdb 100644 --- a/src/test/java/org/squiddev/cobalt/compiler/CompilerUnitTests.java +++ b/src/test/java/org/squiddev/cobalt/compiler/CompilerUnitTests.java @@ -30,7 +30,10 @@ import org.squiddev.cobalt.Print; import org.squiddev.cobalt.Prototype; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.OutputStream; import java.nio.file.Files; import java.nio.file.Paths; @@ -84,10 +87,8 @@ private static void compareResults(String dir, String file) throws IOException, } private static String dumpState(Prototype p) { - StringWriter output = new StringWriter(); - try (PrintWriter ps = new PrintWriter(output)) { - Print.printFunction(ps, p, true, false); - } + StringBuilder output = new StringBuilder(); + Print.printFunction(output, p, true, false); return output.toString(); } }