arcticfox1919 / LuaDardo

A Lua virtual machine written in Dart
Apache License 2.0
172 stars 32 forks source link

show line numbers for runtime errors? #33

Open thauschildt opened 5 months ago

thauschildt commented 5 months ago

Is it possible to get the line number in which a runtime error occurs?

If I run the code:

try {
  ls.loadString("x=123 \n x()");
  ls.call(0, 0);
} catch (e) {
  print(e);
}

only the error message "Exception: not a function" is thrown.

For debugging larger scripts, it would be nice if the line number, in which the error occured, would be shown.

Snowcrash5 commented 3 months ago

Hey, I made a custom solution that works for my purposes, but I'm not making a pull request because I dont know if it covers everything / works generally

  1. Replaced _runLuaClosure with

    void _runLuaClosure() {
    try {
      for (;;) {
        int i = fetch();
        OpCode opCode = Instruction.getOpCode(i);
        opCode.action!.call(i, this);
        if (opCode.name == "RETURN") {
          break;
        }
      }
    } on LuaClosureException catch (_) {
      rethrow;
    } catch (exception) {
      // When a DART function is called by lua, then we want to use the PREV lua stack, as this one is a fake
      // one which does not contain proto source info. The easiest way to do this is if the closure proto is null, get the previous one.
      final targetStack = _stack?.closure?.proto == null ? _stack?.prev : _stack;
      if(targetStack == null){
        rethrow;
      }
    
      // Get this kind of stack trace only if source data is available.
      final prototype = targetStack.closure?.proto;
      if (prototype == null ||
          prototype.source == null ||
          prototype.lineDefined == null ||
          prototype.lastLineDefined == null) {
        rethrow;
      }
    
      throw LuaClosureException.fromPrototype(exception, prototype, targetStack.pc);
    }
    }

    You can omit the lineDefined and lastLineDefined checks, I guess. They signify the start and end lines of the current closure. Originally I had the exception class get that entire code block, but now I only have the exception line and a few after and few before.

  2. Implement an exception class. You can change the line range for more than -3/3, right now its hard coded.

class LuaClosureException { LuaClosureException( {required this.base, required this.exceptionSrc, required this.exceptionLine});

Object base; String exceptionSrc; int exceptionLine;

factory LuaClosureException.fromPrototype( Object baseException, Prototype proto, int luaPc) { final lineNumber = proto.lineInfo[luaPc]; final source = _getLinesRange( proto.source!, lineNumber - 3, lineNumber + 3, lineNumber);

return LuaClosureException(
    base: baseException, exceptionSrc: source, exceptionLine: lineNumber);

}

@override String toString() { return "Unhandled LUA exception in line $exceptionLine:\n$exceptionSrc"; }

// Helper string function static String _getLinesRange( String input, int startLine, int endLine, int targetLine) { // Counter for the current line number int currentLine = 1;

// StringBuffer to build the result efficiently
StringBuffer result = StringBuffer();

// Variables to track line start and whether we are within the desired range
bool inRange = false;

// Iterate over each character in the string
for (int i = 0; i < input.length; i++) {
  // Check if we are starting a new line
  if (currentLine == startLine) {
    inRange = true; // Start appending lines to the result
  }

  // Append the current character if it's within the range
  if (inRange) {
    result.write(input[i]);
  }

  // Check for newline characters to increment the line count
  if (input[i] == '\n') {
    if (inRange && currentLine >= endLine) {
      break; // Stop if the end line has been reached
    }
    if (currentLine == targetLine - 1) {
      result.write("==>");
    }

    currentLine++;
  }
}

return result.toString();

} }



Hope this helps :)