content: /*******************************************************************************
   * Copyright (c) 2009-2011 Luaj.org. All rights reserved.
   * <p/>
   * 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:
   * <p/>
   * The above copyright notice and this permission notice shall be included in
   * all copies or substantial portions of the Software.
   * <p/>
   * 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.vm2.lib;
  
  //TODO 与3.0不一样
  
  import org.luaj.vm2.Globals;
  import org.luaj.vm2.Lua;
  import org.luaj.vm2.LuaBoolean;
  import org.luaj.vm2.LuaClosure;
  import org.luaj.vm2.LuaError;
  import org.luaj.vm2.LuaFunction;
  import org.luaj.vm2.LuaNil;
  import org.luaj.vm2.LuaNumber;
  import org.luaj.vm2.LuaString;
  import org.luaj.vm2.LuaTable;
  import org.luaj.vm2.LuaThread;
  import org.luaj.vm2.LuaUserdata;
  import org.luaj.vm2.LuaValue;
  import org.luaj.vm2.Prototype;
  import org.luaj.vm2.Varargs;
  import org.luaj.vm2.log.LuaPrint;
  
  /**
   * Subclass of {@link LibFunction} which implements the lua standard {@code debug}
   * library.
   * <p/>
   * The debug library in luaj tries to emulate the behavior of the corresponding C-based lua library.
   * To do this, it must maintain a separate stack of calls to {@link LuaClosure} and {@link LibFunction}
   * instances.
   * Especially when lua-to-java bytecode compiling is being used
   * via a {@link LuaCompiler} such as {@link LuaJC},
   * this cannot be done in all cases.
   * <p/>
   * Typically, this library is included as part of a call to either
   * {@link JsePlatform#debugGlobals()} or {@link JmePlatform#debugGlobals()}
   * <pre> {@code
   * Globals globals = JsePlatform.debugGlobals();
   * System.out.println( globals.get("debug").get("traceback").call() );
   * } </pre>
   * <p/>
   * To instantiate and use it directly,
   * link it into your globals table via {@link LuaValue#load(LuaValue)} using code such as:
   * <pre> {@code
   * Globals globals = new Globals();
   * globals.load(new JseBaseLib());
   * globals.load(new PackageLib());
   * globals.load(new DebugLib());
   * System.out.println( globals.get("debug").get("traceback").call() );
   * } </pre>
   * <p/>
   *
   * @see LibFunction
   * @see JsePlatform
   * @see JmePlatform
   * @see <a href="http://www.lua.org/manual/5.2/manual.html#6.10">Lua 5.2 Debug Lib Reference</a>
   */
  public class DebugLib extends TwoArgFunction {
      public static boolean CALLS;
      public static boolean TRACE;
  
      static {
          try {
              CALLS = (null != System.getProperty("CALLS"));
          } catch (Exception e) {
          }
          try {
              TRACE = (null != System.getProperty("TRACE"));
          } catch (Exception e) {
          }
      }
  
      private static final LuaString LUA = valueOf("Lua");
      private static final LuaString QMARK = valueOf("?");
      private static final LuaString CALL = valueOf("call");
      private static final LuaString LINE = valueOf("line");
      private static final LuaString COUNT = valueOf("count");
      private static final LuaString RETURN = valueOf("return");
  
      private static final LuaString FUNC = valueOf("func");
      private static final LuaString ISTAILCALL = valueOf("istailcall");
      private static final LuaString ISVARARG = valueOf("isvararg");
      private static final LuaString NUPS = valueOf("nups");
      private static final LuaString NPARAMS = valueOf("nparams");
      private static final LuaString NAME = valueOf("name");
      private static final LuaString NAMEWHAT = valueOf("namewhat");
      private static final LuaString WHAT = valueOf("what");
      private static final LuaString SOURCE = valueOf("source");
      private static final LuaString SHORT_SRC = valueOf("short_src");
      private static final LuaString LINEDEFINED = valueOf("linedefined");
      private static final LuaString LASTLINEDEFINED = valueOf("lastlinedefined");
      private static final LuaString CURRENTLINE = valueOf("currentline");
      private static final LuaString ACTIVELINES = valueOf("activelines");
  
      Globals globals;
  
      public LuaValue call(LuaValue modname, LuaValue env) {
          globals = env.checkglobals();
          globals.debuglib = this;
          LuaTable debug = new LuaTable();
          debug.set("debug", new debug());
          debug.set("gethook", new gethook());
          debug.set("getinfo", new getinfo());
          debug.set("getlocal", new getlocal());
          debug.set("getmetatable", new getmetatable());
          debug.set("getregistry", new getregistry());
          debug.set("getupvalue", new getupvalue());
          debug.set("getuservalue", new getuservalue());
          debug.set("sethook", new sethook());
          debug.set("setlocal", new setlocal());
          debug.set("setmetatable", new setmetatable());
          debug.set("setupvalue", new setupvalue());
          debug.set("setuservalue", new setuservalue());
          debug.set("traceback", new traceback());
          debug.set("upvalueid", new upvalueid());
          debug.set("upvaluejoin", new upvaluejoin());
          debug.set("traceback_count", new tracebackCount());
  
          //extend for luaview
          new com.taobao.luaview.vm.extend.DebugLib(this, globals).extend(debug);
  
          env.set("debug", debug);
          env.get("package").get("loaded").set("debug", debug);
          return debug;
      }
  
      //----------------------------------------------------------------------------------------------
  
      final class readCmd extends ZeroArgFunction {
          @Override
          public LuaValue call() {
              if (globals.debugConnection != null) {
                  String cmd = globals.debugConnection.reciveCMD();
                  return cmd != null ? valueOf(cmd) : NIL;
              } else {
                  return NIL;
              }
          }
      }
  
      final class sleep extends OneArgFunction {
          @Override
          public LuaValue call(LuaValue arg) {
              try {
                  Thread.sleep(arg.checklong());
              } catch (InterruptedException e) {
                  e.printStackTrace();
              }
              return NIL;
          }
      }
  
      final class printToServer extends OneArgFunction {
          @Override
          public LuaValue call(LuaValue arg) {
              if (globals.debugConnection != null) {
                  globals.debugConnection.sendingEnabled = arg.optboolean(false);
              }
              return NIL;
          }
      }
  
      final class runningLine extends TwoArgFunction {
          @Override
          public LuaValue call(LuaValue arg1, LuaValue arg2) {
              String fileName = arg1.checkjstring(1);
              String lineNumber = String.valueOf(arg2.checkint());
  
              if (globals.debugConnection != null) {
                  globals.debugConnection.sendCmd("running", fileName, lineNumber.getBytes());
              }
  
              return NIL;
          }
      }
  
      final class get_file_line extends ZeroArgFunction {
          @Override
          public LuaValue call() {
              return NIL;
          }
      }
  
      //----------------------------------------------------------------------------------------------
  
      // debug.debug()
      static final class debug extends ZeroArgFunction {
          public LuaValue call() {
              return NONE;
          }
      }
  
      // debug.gethook ([thread])
      final class gethook extends VarArgFunction {
          public Varargs invoke(Varargs args) {
              LuaThread t = args.narg() > 0 ? args.checkthread(1) : globals.running;
              return varargsOf(
                      t.hookfunc != null ? t.hookfunc : NIL,
                      valueOf((t.hookcall ? "c" : "") + (t.hookline ? "l" : "") + (t.hookrtrn ? "r" : "")),
                      valueOf(t.hookcount));
          }
      }
  
      //	debug.getinfo ([thread,] f [, what])
      final class getinfo extends VarArgFunction {
          public Varargs invoke(Varargs args) {
              int a = 1;
              LuaThread thread = args.isthread(a) ? args.checkthread(a++) : globals.running;
              LuaValue func = args.arg(a++);
              String what = args.optjstring(a++, "flnStu");
              DebugLib.CallStack callstack = callstack(thread);
  
              // find the stack info
              DebugLib.CallFrame frame;
              if (func.isnumber()) {
                  frame = callstack.getCallFrame(func.toint());
                  if (frame == null)
                      return NONE;
                  func = frame.f;
              } else if (func.isfunction()) {
                  frame = callstack.findCallFrame(func);
              } else {
                  return argerror(a - 2, "function or level");
              }
  
              // start a table
              DebugInfo ar = callstack.auxgetinfo(what, (LuaFunction) func, frame);
              LuaTable info = new LuaTable();
              if (what.indexOf('S') >= 0) {
                  info.set(WHAT, LUA);
                  info.set(SOURCE, valueOf(ar.source));
                  info.set(SHORT_SRC, valueOf(ar.short_src));
                  info.set(LINEDEFINED, valueOf(ar.linedefined));
                  info.set(LASTLINEDEFINED, valueOf(ar.lastlinedefined));
              }
              if (what.indexOf('l') >= 0) {
                  info.set(CURRENTLINE, valueOf(ar.currentline));
              }
              if (what.indexOf('u') >= 0) {
                  info.set(NUPS, valueOf(ar.nups));
                  info.set(NPARAMS, valueOf(ar.nparams));
                  info.set(ISVARARG, ar.isvararg ? ONE : ZERO);
              }
              if (what.indexOf('n') >= 0) {
                  info.set(NAME, LuaValue.valueOf(ar.name != null ? ar.name : "?"));
                  info.set(NAMEWHAT, LuaValue.valueOf(ar.namewhat));
              }
              if (what.indexOf('t') >= 0) {
                  info.set(ISTAILCALL, ZERO);
              }
              if (what.indexOf('L') >= 0) {
                  LuaTable lines = new LuaTable();
                  info.set(ACTIVELINES, lines);
                  DebugLib.CallFrame cf;
                  for (int l = 1; (cf = callstack.getCallFrame(l)) != null; ++l)
                      if (cf.f == func)
                          lines.insert(-1, valueOf(cf.currentline()));
              }
              if (what.indexOf('f') >= 0) {
                  if (func != null)
                      info.set(FUNC, func);
              }
              return info;
          }
      }
  
      //	debug.getlocal ([thread,] f, local)
      final class getlocal extends VarArgFunction {
          public Varargs invoke(Varargs args) {
              int a = 1;
              LuaThread thread = args.isthread(a) ? args.checkthread(a++) : globals.running;
              int level = args.checkint(a++);
              int local = args.checkint(a++);
              CallFrame f = callstack(thread).getCallFrame(level);
              return f != null ? f.getLocal(local) : NONE;
          }
      }
  
      //	debug.getmetatable (value)
      final class getmetatable extends LibFunction {
          public LuaValue call(LuaValue v) {
              LuaValue mt = v.getmetatable();
              return mt != null ? mt : NIL;
          }
      }
  
      //	debug.getregistry ()
      final class getregistry extends ZeroArgFunction {
          public LuaValue call() {
              return globals;
          }
      }
  
      //	debug.getupvalue (f, up)
      static final class getupvalue extends VarArgFunction {
          public Varargs invoke(Varargs args) {
              LuaValue func = args.checkfunction(1);
              int up = args.checkint(2);
              if (func instanceof LuaClosure) {
                  LuaClosure c = (LuaClosure) func;
                  LuaString name = findupvalue(c, up);
                  if (name != null) {
                      return varargsOf(name, c.upValues[up - 1].getValue());
                  }
              }
              return NIL;
          }
      }
  
      //	debug.getuservalue (u)
      static final class getuservalue extends LibFunction {
          public LuaValue call(LuaValue u) {
              return u.isuserdata() ? u : NIL;
          }
      }
  
  
      // debug.sethook ([thread,] hook, mask [, count])
      final class sethook extends VarArgFunction {
          public Varargs invoke(Varargs args) {
              int a = 1;
              LuaThread t = args.isthread(a) ? args.checkthread(a++) : globals.running;
              LuaValue func = args.optfunction(a++, null);
              String str = args.optjstring(a++, "");
              int count = args.optint(a++, 0);
              boolean call = false, line = false, rtrn = false;
              for (int i = 0; i < str.length(); i++)
                  switch (str.charAt(i)) {
                      case 'c':
                          call = true;
                          break;
                      case 'l':
                          line = true;
                          break;
                      case 'r':
                          rtrn = true;
                          break;
                  }
              t.hookfunc = func;
              t.hookcall = call;
              t.hookline = line;
              t.hookcount = count;
              t.hookrtrn = rtrn;
              return NONE;
          }
      }
  
      //	debug.setlocal ([thread,] level, local, value)
      final class setlocal extends VarArgFunction {
          public Varargs invoke(Varargs args) {
              int a = 1;
              LuaThread thread = args.isthread(a) ? args.checkthread(a++) : globals.running;
              int level = args.checkint(a++);
              int local = args.checkint(a++);
              LuaValue value = args.arg(a++);
              CallFrame f = callstack(thread).getCallFrame(level);
              return f != null ? f.setLocal(local, value) : NONE;
          }
      }
  
      //	debug.setmetatable (value, table)
      final class setmetatable extends TwoArgFunction {
          public LuaValue call(LuaValue value, LuaValue table) {
              LuaValue mt = table.opttable(null);
              switch (value.type()) {
                  case TNIL:
                      LuaNil.s_metatable = mt;
                      break;
                  case TNUMBER:
                      LuaNumber.s_metatable = mt;
                      break;
                  case TBOOLEAN:
                      LuaBoolean.s_metatable = mt;
                      break;
                  case TSTRING:
                      LuaString.s_metatable = mt;
                      break;
                  case TFUNCTION:
                      LuaFunction.s_metatable = mt;
                      break;
                  case TTHREAD:
                      LuaThread.s_metatable = mt;
                      break;
                  default:
                      value.setmetatable(mt);
              }
              return value;
          }
      }
  
      //	debug.setupvalue (f, up, value)
      final class setupvalue extends VarArgFunction {
          public Varargs invoke(Varargs args) {
              LuaValue func = args.checkfunction(1);
              int up = args.checkint(2);
              LuaValue value = args.arg(3);
              if (func instanceof LuaClosure) {
                  LuaClosure c = (LuaClosure) func;
                  LuaString name = findupvalue(c, up);
                  if (name != null) {
                      c.upValues[up - 1].setValue(value);
                      return name;
                  }
              }
              return NIL;
          }
      }
  
      //	debug.setuservalue (udata, value)
      final class setuservalue extends VarArgFunction {
          public Varargs invoke(Varargs args) {
              Object o = args.checkuserdata(1);
              LuaValue v = args.checkvalue(2);
              LuaUserdata u = (LuaUserdata) args.arg1();
              u.m_instance = v.checkuserdata();
              u.m_metatable = v.getmetatable();
              return NONE;
          }
      }
  
      //	debug.traceback ([thread,] [message [, level]])
      final class traceback extends VarArgFunction {
          public Varargs invoke(Varargs args) {
              int a = 1;
              LuaThread thread = args.isthread(a) ? args.checkthread(a++) : globals.running;
              String message = args.optjstring(a++, null);
              int level = args.optint(a++, 1);
              String tb = callstack(thread).traceback(level);
              return valueOf(message != null ? message + "\n" + tb : tb);
          }
      }
  
      //	debug.upvalueid (f, n)
      final class upvalueid extends VarArgFunction {
          public Varargs invoke(Varargs args) {
              LuaValue func = args.checkfunction(1);
              int up = args.checkint(2);
              if (func instanceof LuaClosure) {
                  LuaClosure c = (LuaClosure) func;
                  if (c.upValues != null && up > 0 && up <= c.upValues.length) {
                      return valueOf(c.upValues[up - 1].hashCode());
                  }
              }
              return NIL;
          }
      }
  
      //	debug.upvaluejoin (f1, n1, f2, n2)
      final class upvaluejoin extends VarArgFunction {
          public Varargs invoke(Varargs args) {
              LuaClosure f1 = args.checkclosure(1);
              int n1 = args.checkint(2);
              LuaClosure f2 = args.checkclosure(3);
              int n2 = args.checkint(4);
              if (n1 < 1 || n1 > f1.upValues.length)
                  argerror("index out of range");
              if (n2 < 1 || n2 > f2.upValues.length)
                  argerror("index out of range");
              f1.upValues[n1 - 1] = f2.upValues[n2 - 1];
              return NONE;
          }
      }
  
      final class tracebackCount extends ZeroArgFunction {
          public LuaValue call() {
              int calls = globals.debuglib.callstack().calls;
              return valueOf(calls > 0 ? (calls - 1) : 0);
          }
      }
  
      public void onCall(LuaFunction f) {
          LuaThread t = globals.running;
          callstack().onCall(f);
  
          if (t.inhook) return;
          if (t.hookcall && t.hookfunc != null)
              callHook(CALL, NIL);
      }
  
      public void onCall(LuaClosure c, Varargs varargs, LuaValue[] stack) {
          LuaThread t = globals.running;
          callstack().onCall(c, varargs, stack);
  
          if (t.inhook) return;
          if (t.hookcall && t.hookfunc != null)
              callHook(CALL, NIL);
      }
  
      public void onInstruction(int pc, Varargs v, int top) {
          LuaThread t = globals.running;
          callstack().onInstruction(pc, v, top);
  
          if (t.inhook) return;
          if (t.hookfunc == null) return;
          if (t.hookcount > 0)
              if (++t.bytecodes % t.hookcount == 0)
                  callHook(COUNT, NIL);
          if (t.hookline) {
              int newline = callstack().currentline();
              if (newline != t.lastline) {
                  t.lastline = newline;
                  callHook(LINE, LuaValue.valueOf(newline));
              }
          }
      }
  
      public void onReturn() {
          LuaThread t = globals.running;
          callstack().onReturn();
  
          if (t.inhook) return;
          if (t.hookcall && t.hookfunc != null)
              callHook(RETURN, NIL);
      }
  
      public String traceback(int level) {
          return callstack().traceback(level);
      }
  
      void callHook(LuaValue type, LuaValue arg) {
          LuaThread t = globals.running;
          t.inhook = true;
          try {
              t.hookfunc.call(type, arg);
          } catch (LuaError e) {
              throw e;
          } catch (RuntimeException e) {
              throw new LuaError(e);
          } finally {
              t.inhook = false;
          }
      }
  
      CallStack callstack() {
          return callstack(globals.running);
      }
  
      CallStack callstack(LuaThread t) {
          if (t.callstack == null)
              t.callstack = new CallStack();
          return (CallStack) t.callstack;
      }
  
      static class DebugInfo {
          String name;	/* (n) */
          String namewhat;	/* (n) 'global', 'local', 'field', 'method' */
          String what;	/* (S) 'Lua', 'C', 'main', 'tail' */
          String source;	/* (S) */
          int currentline;	/* (l) */
          int linedefined;	/* (S) */
          int lastlinedefined;	/* (S) */
          short nups;	/* (u) number of upvalues */
          short nparams;/* (u) number of parameters */
          boolean isvararg;        /* (u) */
          boolean istailcall;	/* (t) */
          String short_src; /* (S) */
          CallFrame cf;  /* active function */
  
          public void funcinfo(LuaFunction f) {
              if (f.isclosure()) {
                  Prototype p = f.checkclosure().p;
                  this.source = p.source != null ? p.source.tojstring() : "=?";
                  this.linedefined = p.linedefined;
                  this.lastlinedefined = p.lastlinedefined;
                  this.what = (this.linedefined == 0) ? "main" : "Lua";
                  this.short_src = p.shortsource();
              } else {
                  this.source = "=[Java]";
                  this.linedefined = -1;
                  this.lastlinedefined = -1;
                  this.what = "Java";
                  this.short_src = f.name();
              }
          }
      }
  
      public static class CallStack {
          final static CallFrame[] EMPTY = {};
          CallFrame[] frame = EMPTY;
          int calls = 0;
  
          CallStack() {
          }
  
          int currentline() {
              return calls > 0 ? frame[calls - 1].currentline() : -1;
          }
  
          private CallFrame pushcall() {
              if (calls >= frame.length) {
                  int n = Math.max(4, frame.length * 3 / 2);
                  CallFrame[] f = new CallFrame[n];
                  System.arraycopy(frame, 0, f, 0, frame.length);
                  for (int i = frame.length; i < n; ++i)
                      f[i] = new CallFrame();
                  frame = f;
                  for (int i = 1; i < n; ++i)
                      f[i].previous = f[i - 1];
              }
              return frame[calls++];
          }
  
          final void onCall(LuaFunction function) {
              pushcall().set(function);
          }
  
          final void onCall(LuaClosure function, Varargs varargs, LuaValue[] stack) {
              pushcall().set(function, varargs, stack);
          }
  
          final void onReturn() {
              if (calls > 0)
                  frame[--calls].reset();
          }
  
          final void onInstruction(int pc, Varargs v, int top) {
              frame[calls - 1].instr(pc, v, top);
          }
  
          /**
           * Get the traceback starting at a specific level.
           *
           * @param level
           * @return String containing the traceback.
           */
          String traceback(int level) {
              StringBuffer sb = new StringBuffer();
              sb.append("stack traceback:");
              for (DebugLib.CallFrame c; (c = getCallFrame(level++)) != null; ) {
                  sb.append("\n\t");
                  sb.append(c.shortsource());
                  sb.append(':');
                  if (c.currentline() > 0)
                      sb.append(c.currentline() + ":");
                  sb.append(" in ");
                  DebugInfo ar = auxgetinfo("n", c.f, c);
                  if (c.linedefined() == 0)
                      sb.append("main chunk");
                  else if (ar.name != null) {
                      sb.append("function '");
                      sb.append(ar.name);
                      sb.append('\'');
                  } else {
                      sb.append("function <" + c.shortsource() + ":" + c.linedefined() + ">");
                  }
              }
              sb.append("\n\t[Java]: in ?");
              return sb.toString();
          }
  
          DebugLib.CallFrame getCallFrame(int level) {
              if (level < 1 || level > calls)
                  return null;
              return frame[calls - level];
          }
  
          DebugLib.CallFrame findCallFrame(LuaValue func) {
              for (int i = 1; i <= calls; ++i)
                  if (frame[calls - i].f == func)
                      return frame[i];
              return null;
          }
  
  
          DebugInfo auxgetinfo(String what, LuaFunction f, CallFrame ci) {
              DebugInfo ar = new DebugInfo();
              for (int i = 0, n = what.length(); i < n; ++i) {
                  switch (what.charAt(i)) {
                      case 'S':
                          ar.funcinfo(f);
                          break;
                      case 'l':
                          ar.currentline = ci != null && ci.f.isclosure() ? ci.currentline() : -1;
                          break;
                      case 'u':
                          if (f != null && f.isclosure()) {
                              Prototype p = f.checkclosure().p;
                              ar.nups = (short) p.upvalues.length;
                              ar.nparams = (short) p.numparams;
                              ar.isvararg = p.is_vararg != 0;
                          } else {
                              ar.nups = 0;
                              ar.isvararg = true;
                              ar.nparams = 0;
                          }
                          break;
                      case 't':
                          ar.istailcall = false;
                          break;
                      case 'n': {
                        /* calling function is a known Lua function? */
                          if (ci != null && ci.previous != null) {
                              if (ci.previous.f.isclosure()) {
                                  NameWhat nw = getfuncname(ci.previous);
                                  if (nw != null) {
                                      ar.name = nw.name;
                                      ar.namewhat = nw.namewhat;
                                  }
                              }
                          }
                          if (ar.namewhat == null) {
                              ar.namewhat = "";  /* not found */
                              ar.name = null;
                          }
                          break;
                      }
                      case 'L':
                      case 'f':
                          break;
                      default:
                          // TODO: return bad status.
                          break;
                  }
              }
              return ar;
          }
  
      }
  
      static class CallFrame {
          LuaFunction f;
          int pc;
          int top;
          Varargs v;
          LuaValue[] stack;
          CallFrame previous;
  
          void set(LuaClosure function, Varargs varargs, LuaValue[] stack) {
              this.f = function;
              this.v = varargs;
              this.stack = stack;
          }
  
          public String shortsource() {
              return f.isclosure() ? f.checkclosure().p.shortsource() : "[Java]";
          }
  
          void set(LuaFunction function) {
              this.f = function;
          }
  
          void reset() {
              this.f = null;
              this.v = null;
              this.stack = null;
          }
  
          void instr(int pc, Varargs v, int top) {
              this.pc = pc;
              this.v = v;
              this.top = top;
              if (TRACE) {
                  new LuaPrint().buildState(f.checkclosure(), pc, stack, top, v).print();
                  //Print.printState(f.checkclosure(), pc, stack, top, v);
              }
          }
  
          Varargs getLocal(int i) {
              LuaString name = getlocalname(i);
              if (name != null)
                  return varargsOf(name, stack[i - 1]);
              else
                  return NIL;
          }
  
          Varargs setLocal(int i, LuaValue value) {
              LuaString name = getlocalname(i);
              if (name != null) {
                  stack[i - 1] = value;
                  return name;
              } else {
                  return NIL;
              }
          }
  
          int currentline() {
              if (!f.isclosure()) return -1;
              int[] li = f.checkclosure().p.lineinfo;
              return li == null || pc < 0 || pc >= li.length ? -1 : li[pc];
          }
  
          String sourceline() {
              if (!f.isclosure()) return f.tojstring();
              return f.checkclosure().p.shortsource() + ":" + currentline();
          }
  
          private int linedefined() {
              return f.isclosure() ? f.checkclosure().p.linedefined : -1;
          }
  
          LuaString getlocalname(int index) {
              if (!f.isclosure()) return null;
              return f.checkclosure().p.getlocalname(index, pc);
          }
      }
  
      static LuaString findupvalue(LuaClosure c, int up) {
          if (c.upValues != null && up > 0 && up <= c.upValues.length) {
              if (c.p.upvalues != null && up <= c.p.upvalues.length)
                  return c.p.upvalues[up - 1].name;
              else
                  return LuaString.valueOf("." + up);
          }
          return null;
      }
  
      static void lua_assert(boolean x) {
          if (!x) throw new RuntimeException("lua_assert failed");
      }
  
      static class NameWhat {
          final String name;
          final String namewhat;
  
          NameWhat(String name, String namewhat) {
              this.name = name;
              this.namewhat = namewhat;
          }
      }
  
      // Return the name info if found, or null if no useful information could be found.
      static NameWhat getfuncname(DebugLib.CallFrame frame) {
          if (!frame.f.isclosure())
              return new NameWhat(frame.f.classnamestub(), "Java");
          Prototype p = frame.f.checkclosure().p;
          int pc = frame.pc;
          int i = p.code[pc]; /* calling instruction */
          LuaString tm;
          switch (Lua.GET_OPCODE(i)) {
              case Lua.OP_CALL:
              case Lua.OP_TAILCALL: /* get function name */
                  return getobjname(p, pc, Lua.GETARG_A(i));
              case Lua.OP_TFORCALL: /* for iterator */
                  return new NameWhat("(for iterator)", "(for iterator");
              /* all other instructions can call only through metamethods */
              case Lua.OP_SELF:
              case Lua.OP_GETTABUP:
              case Lua.OP_GETTABLE:
                  tm = LuaValue.INDEX;
                  break;
              case Lua.OP_SETTABUP:
              case Lua.OP_SETTABLE:
                  tm = LuaValue.NEWINDEX;
                  break;
              case Lua.OP_EQ:
                  tm = LuaValue.EQ;
                  break;
              case Lua.OP_ADD:
                  tm = LuaValue.ADD;
                  break;
              case Lua.OP_SUB:
                  tm = LuaValue.SUB;
                  break;
              case Lua.OP_MUL:
                  tm = LuaValue.MUL;
                  break;
              case Lua.OP_DIV:
                  tm = LuaValue.DIV;
                  break;
              case Lua.OP_MOD:
                  tm = LuaValue.MOD;
                  break;
              case Lua.OP_POW:
                  tm = LuaValue.POW;
                  break;
              case Lua.OP_UNM:
                  tm = LuaValue.UNM;
                  break;
              case Lua.OP_LEN:
                  tm = LuaValue.LEN;
                  break;
              case Lua.OP_LT:
                  tm = LuaValue.LT;
                  break;
              case Lua.OP_LE:
                  tm = LuaValue.LE;
                  break;
              case Lua.OP_CONCAT:
                  tm = LuaValue.CONCAT;
                  break;
              default:
                  return null;  /* else no useful name can be found */
          }
          return new NameWhat(tm.tojstring(), "metamethod");
      }
  
      // return NameWhat if found, null if not
      public static NameWhat getobjname(Prototype p, int lastpc, int reg) {
          int pc = lastpc; // currentpc(L, ci);
          LuaString name = p.getlocalname(reg + 1, pc);
          if (name != null) /* is a local? */
              return new NameWhat(name.tojstring(), "local");
  
  		/* else try symbolic execution */
          pc = findsetreg(p, lastpc, reg);
          if (pc != -1) { /* could find instruction? */
              int i = p.code[pc];
              switch (Lua.GET_OPCODE(i)) {
                  case Lua.OP_MOVE: {
                      int a = Lua.GETARG_A(i);
                      int b = Lua.GETARG_B(i); /* move from `b' to `a' */
                      if (b < a)
                          return getobjname(p, pc, b); /* get name for `b' */
                      break;
                  }
                  case Lua.OP_GETTABUP:
                  case Lua.OP_GETTABLE: {
                      int k = Lua.GETARG_C(i); /* key index */
                      int t = Lua.GETARG_B(i); /* table index */
                      LuaString vn = (Lua.GET_OPCODE(i) == Lua.OP_GETTABLE)  /* name of indexed variable */
                              ? p.getlocalname(t + 1, pc)
                              : (t < p.upvalues.length ? p.upvalues[t].name : QMARK);
                      name = kname(p, k);
                      return new NameWhat(name.tojstring(), vn != null && vn.eq_b(ENV) ? "global" : "field");
                  }
                  case Lua.OP_GETUPVAL: {
                      int u = Lua.GETARG_B(i); /* upvalue index */
                      name = u < p.upvalues.length ? p.upvalues[u].name : QMARK;
                      return new NameWhat(name.tojstring(), "upvalue");
                  }
                  case Lua.OP_LOADK:
                  case Lua.OP_LOADKX: {
                      int b = (Lua.GET_OPCODE(i) == Lua.OP_LOADK) ? Lua.GETARG_Bx(i)
                              : Lua.GETARG_Ax(p.code[pc + 1]);
                      if (p.k[b].isstring()) {
                          name = p.k[b].strvalue();
                          return new NameWhat(name.tojstring(), "constant");
                      }
                      break;
                  }
                  case Lua.OP_SELF: {
                      int k = Lua.GETARG_C(i); /* key index */
                      name = kname(p, k);
                      return new NameWhat(name.tojstring(), "method");
                  }
                  default:
                      break;
              }
          }
          return null; /* no useful name found */
      }
  
      static LuaString kname(Prototype p, int c) {
          if (Lua.ISK(c) && p.k[Lua.INDEXK(c)].isstring())
              return p.k[Lua.INDEXK(c)].strvalue();
          else
              return QMARK;
      }
  
      /*
      ** try to find last instruction before 'lastpc' that modified register 'reg'
      */
      static int findsetreg(Prototype p, int lastpc, int reg) {
          int pc;
          int setreg = -1;  /* keep last instruction that changed 'reg' */
          for (pc = 0; pc < lastpc; pc++) {
              int i = p.code[pc];
              int op = Lua.GET_OPCODE(i);
              int a = Lua.GETARG_A(i);
              switch (op) {
                  case Lua.OP_LOADNIL: {
                      int b = Lua.GETARG_B(i);
                      if (a <= reg && reg <= a + b)  /* set registers from 'a' to 'a+b' */
                          setreg = pc;
                      break;
                  }
                  case Lua.OP_TFORCALL: {
                      if (reg >= a + 2) setreg = pc;  /* affect all regs above its base */
                      break;
                  }
                  case Lua.OP_CALL:
                  case Lua.OP_TAILCALL: {
                      if (reg >= a) setreg = pc;  /* affect all registers above base */
                      break;
                  }
                  case Lua.OP_JMP: {
                      int b = Lua.GETARG_sBx(i);
                      int dest = pc + 1 + b;
              /* jump is forward and do not skip `lastpc'? */
                      if (pc < dest && dest <= lastpc)
                          pc += b;  /* do the jump */
                      break;
                  }
                  case Lua.OP_TEST: {
                      if (reg == a) setreg = pc;  /* jumped code can change 'a' */
                      break;
                  }
                  default:
                      if (Lua.testAMode(op) && reg == a)  /* any instruction that set A */
                          setreg = pc;
                      break;
              }
          }
          return setreg;
      }
  }
  