| /******************************************************************************* |
| * Copyright (c) 2009 Luaj.org. All rights reserved. |
| * |
| * Permission is hereby granted, free of charge, to any person obtaining a copy |
| * of this software and associated documentation files (the "Software"), to deal |
| * in the Software without restriction, including without limitation the rights |
| * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
| * copies of the Software, and to permit persons to whom the Software is |
| * furnished to do so, subject to the following conditions: |
| * |
| * The above copyright notice and this permission notice shall be included in |
| * all copies or substantial portions of the Software. |
| * |
| * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
| * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
| * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
| * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
| * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
| * THE SOFTWARE. |
| ******************************************************************************/ |
| package org.luaj.vm3; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.PrintStream; |
| |
| /** |
| * Debug helper class to pretty-print lua bytecodes. |
| * @see Prototype |
| * @see LuaClosure |
| */ |
| public class Print extends Lua { |
| |
| /** opcode names */ |
| private static final String STRING_FOR_NULL = "null"; |
| public static PrintStream ps = System.out; |
| |
| /** String names for each lua opcode value. */ |
| public static final String[] OPNAMES = { "MOVE", "LOADK", "LOADKX", "LOADBOOL", "LOADNIL", "GETUPVAL", "GETTABUP", "GETTABLE", "SETTABUP", "SETUPVAL", "SETTABLE", "NEWTABLE", "SELF", "ADD", "SUB", "MUL", "DIV", "MOD", "POW", "UNM", "NOT", "LEN", "CONCAT", "JMP", "EQ", "LT", "LE", "TEST", "TESTSET", "CALL", "TAILCALL", "RETURN", "FORLOOP", "FORPREP", "TFORCALL", "TFORLOOP", "SETLIST", "CLOSURE", "VARARG", "EXTRAARG", null, }; |
| |
| static void printString(PrintStream ps, final LuaString s) { |
| |
| ps.print('"'); |
| for (int i = 0, n = s.m_length; i < n; i++) { |
| int c = s.m_bytes[s.m_offset + i]; |
| if (c >= ' ' && c <= '~' && c != '\"' && c != '\\') |
| ps.print((char) c); |
| else { |
| switch (c) { |
| case '"': |
| ps.print("\\\""); |
| break; |
| case '\\': |
| ps.print("\\\\"); |
| break; |
| case 0x0007: /* bell */ |
| ps.print("\\a"); |
| break; |
| case '\b': /* backspace */ |
| ps.print("\\b"); |
| break; |
| case '\f': /* form feed */ |
| ps.print("\\f"); |
| break; |
| case '\t': /* tab */ |
| ps.print("\\t"); |
| break; |
| case '\r': /* carriage return */ |
| ps.print("\\r"); |
| break; |
| case '\n': /* newline */ |
| ps.print("\\n"); |
| break; |
| case 0x000B: /* vertical tab */ |
| ps.print("\\v"); |
| break; |
| default: |
| ps.print('\\'); |
| ps.print(Integer.toString(1000 + 0xff & c).substring(1)); |
| break; |
| } |
| } |
| } |
| ps.print('"'); |
| } |
| |
| static void printValue(PrintStream ps, LuaValue v) { |
| switch (v.type()) { |
| case LuaValue.TSTRING: |
| printString(ps, (LuaString) v); |
| break; |
| default: |
| ps.print(v.tojstring()); |
| |
| } |
| } |
| |
| static void printConstant(PrintStream ps, Prototype f, int i) { |
| printValue(ps, f.k[i]); |
| } |
| |
| static void printUpvalue(PrintStream ps, Upvaldesc u) { |
| ps.print(u.idx + " "); |
| printValue(ps, u.name); |
| } |
| |
| /** |
| * Print the code in a prototype |
| * @param f the {@link Prototype} |
| */ |
| public static void printCode(Prototype f) { |
| int[] code = f.code; |
| int pc, n = code.length; |
| for (pc = 0; pc < n; pc++) { |
| printOpCode(f, pc); |
| ps.println(); |
| } |
| } |
| |
| /** |
| * Print an opcode in a prototype |
| * @param f the {@link Prototype} |
| * @param pc the program counter to look up and print |
| */ |
| public static void printOpCode(Prototype f, int pc) { |
| printOpCode(ps, f, pc); |
| } |
| |
| /** |
| * Print an opcode in a prototype |
| * @param ps the {@link PrintStream} to print to |
| * @param f the {@link Prototype} |
| * @param pc the program counter to look up and print |
| */ |
| public static void printOpCode(PrintStream ps, Prototype f, int pc) { |
| int[] code = f.code; |
| int i = code[pc]; |
| int o = GET_OPCODE(i); |
| int a = GETARG_A(i); |
| int b = GETARG_B(i); |
| int c = GETARG_C(i); |
| int bx = GETARG_Bx(i); |
| int sbx = GETARG_sBx(i); |
| int line = getline(f, pc); |
| ps.print(" " + (pc + 1) + " "); |
| if (line > 0) |
| ps.print("[" + line + "] "); |
| else |
| ps.print("[-] "); |
| ps.print(OPNAMES[o] + " "); |
| 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)); |
| break; |
| case iABx: |
| if (getBMode(o) == OpArgK) { |
| ps.print(a + " " + (-1 - bx)); |
| } else { |
| ps.print(a + " " + (bx)); |
| } |
| break; |
| case iAsBx: |
| if (o == OP_JMP) |
| ps.print(sbx); |
| else |
| ps.print(a + " " + sbx); |
| break; |
| } |
| switch (o) { |
| case OP_LOADK: |
| ps.print(" ; "); |
| printConstant(ps, f, bx); |
| break; |
| case OP_GETUPVAL: |
| case OP_SETUPVAL: |
| ps.print(" ; "); |
| printUpvalue(ps, f.upvalues[b]); |
| break; |
| case OP_GETTABUP: |
| ps.print(" ; "); |
| printUpvalue(ps, f.upvalues[b]); |
| ps.print(" "); |
| if (ISK(c)) |
| printConstant(ps, f, INDEXK(c)); |
| else |
| ps.print("-"); |
| break; |
| case OP_SETTABUP: |
| ps.print(" ; "); |
| printUpvalue(ps, f.upvalues[a]); |
| ps.print(" "); |
| if (ISK(b)) |
| printConstant(ps, f, INDEXK(b)); |
| else |
| ps.print("-"); |
| ps.print(" "); |
| if (ISK(c)) |
| printConstant(ps, f, INDEXK(c)); |
| else |
| ps.print("-"); |
| break; |
| case OP_GETTABLE: |
| case OP_SELF: |
| if (ISK(c)) { |
| ps.print(" ; "); |
| printConstant(ps, f, INDEXK(c)); |
| } |
| break; |
| case OP_SETTABLE: |
| case OP_ADD: |
| case OP_SUB: |
| case OP_MUL: |
| case OP_DIV: |
| case OP_POW: |
| case OP_EQ: |
| case OP_LT: |
| case OP_LE: |
| if (ISK(b) || ISK(c)) { |
| ps.print(" ; "); |
| if (ISK(b)) |
| printConstant(ps, f, INDEXK(b)); |
| else |
| ps.print("-"); |
| ps.print(" "); |
| if (ISK(c)) |
| printConstant(ps, f, INDEXK(c)); |
| else |
| ps.print("-"); |
| } |
| break; |
| case OP_JMP: |
| case OP_FORLOOP: |
| case OP_FORPREP: |
| ps.print(" ; to " + (sbx + pc + 2)); |
| break; |
| case OP_CLOSURE: |
| ps.print(" ; " + f.p[bx].getClass().getName()); |
| break; |
| case OP_SETLIST: |
| if (c == 0) |
| ps.print(" ; " + ((int) code[++pc])); |
| else |
| ps.print(" ; " + ((int) c)); |
| break; |
| case OP_VARARG: |
| ps.print(" ; is_vararg=" + f.is_vararg); |
| break; |
| default: |
| break; |
| } |
| } |
| |
| private static int getline(Prototype f, int pc) { |
| return pc > 0 && f.lineinfo != null && pc < f.lineinfo.length ? f.lineinfo[pc] : -1; |
| } |
| |
| static void printHeader(Prototype f) { |
| String s = String.valueOf(f.source); |
| if (s.startsWith("@") || s.startsWith("=")) |
| s = s.substring(1); |
| else if ("\033Lua".equals(s)) |
| s = "(bstring)"; |
| else |
| 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.numparams + " param, " + f.maxstacksize + " slot, " + f.upvalues.length + " upvalue, "); |
| ps.print(f.locvars.length + " local, " + f.k.length + " constant, " + f.p.length + " function\n"); |
| } |
| |
| static void printConstants(Prototype f) { |
| int i, n = f.k.length; |
| ps.print("constants (" + n + ") for " + id(f) + ":\n"); |
| for (i = 0; i < n; i++) { |
| ps.print(" " + (i + 1) + " "); |
| printValue(ps, f.k[i]); |
| ps.print("\n"); |
| } |
| } |
| |
| static void printLocals(Prototype f) { |
| int i, n = f.locvars.length; |
| ps.print("locals (" + n + ") for " + id(f) + ":\n"); |
| for (i = 0; i < n; i++) { |
| ps.println(" " + i + " " + f.locvars[i].varname + " " + (f.locvars[i].startpc + 1) + " " + (f.locvars[i].endpc + 1)); |
| } |
| } |
| |
| static void printUpValues(Prototype f) { |
| int i, n = f.upvalues.length; |
| ps.print("upvalues (" + n + ") for " + id(f) + ":\n"); |
| for (i = 0; i < n; i++) { |
| ps.print(" " + i + " " + f.upvalues[i] + "\n"); |
| } |
| } |
| |
| /** Pretty-prints contents of a Prototype. |
| * |
| * @param prototype Prototype to print. |
| */ |
| public static void print(Prototype prototype) { |
| printFunction(prototype, true); |
| } |
| |
| /** Pretty-prints contents of a Prototype in short or long form. |
| * |
| * @param prototype Prototype to print. |
| * @param full true to print all fields, false to print short form. |
| */ |
| public static void printFunction(Prototype prototype, boolean full) { |
| int i, n = prototype.p.length; |
| printHeader(prototype); |
| printCode(prototype); |
| if (full) { |
| printConstants(prototype); |
| printLocals(prototype); |
| printUpValues(prototype); |
| } |
| for (i = 0; i < n; i++) |
| printFunction(prototype.p[i], full); |
| } |
| |
| private static void format(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(' '); |
| } |
| } |
| |
| private static String id(Prototype f) { |
| return "Proto"; |
| } |
| |
| private void _assert(boolean b) { |
| if (!b) |
| throw new NullPointerException("_assert failed"); |
| } |
| |
| /** |
| * 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 varargs any {@link Varargs} value that may apply |
| */ |
| public static void printState(LuaClosure cl, int pc, LuaValue[] stack, int top, Varargs varargs) { |
| // print opcode into buffer |
| PrintStream previous = ps; |
| ByteArrayOutputStream baos = new ByteArrayOutputStream(); |
| ps = new PrintStream(baos); |
| printOpCode(cl.p, pc); |
| ps.flush(); |
| ps.close(); |
| ps = previous; |
| format(baos.toString(), 50); |
| |
| // print stack |
| ps.print('['); |
| for (int i = 0; i < stack.length; i++) { |
| LuaValue v = stack[i]; |
| if (v == null) |
| ps.print(STRING_FOR_NULL); |
| else |
| switch (v.type()) { |
| case LuaValue.TSTRING: |
| LuaString s = v.checkstring(); |
| ps.print(s.length() < 48 ? s.tojstring() : s.substring(0, 32).tojstring() + "...+" + (s.length() - 32) + "b"); |
| break; |
| case LuaValue.TFUNCTION: |
| ps.print(v.tojstring()); |
| break; |
| case LuaValue.TUSERDATA: |
| Object o = v.touserdata(); |
| if (o != null) { |
| String n = o.getClass().getName(); |
| n = n.substring(n.lastIndexOf('.') + 1); |
| ps.print(n + ": " + Integer.toHexString(o.hashCode())); |
| } else { |
| ps.print(v.toString()); |
| } |
| break; |
| default: |
| ps.print(v.tojstring()); |
| } |
| if (i + 1 == top) |
| ps.print(']'); |
| ps.print(" | "); |
| } |
| ps.print(varargs); |
| ps.println(); |
| } |
| |
| } |