Search code examples
c++v8

How can I get line number and column from v8 script compile error?


When embedding the V8 Javascript engine in a C++ project, I haven't found any way to get the line number for compilation failure of a script. I have tried the following:

//Given that v8 is initialized, isolate, context and script_source is setup.
v8::TryCatch try_catch{isolate};

//Compile the script from source
v8::Local<v8::Script> script;
if (!v8::Script::Compile(context, script_source).ToLocal(&script)) {
    //Compilation failed

    //Print exception from TryCatch
    auto exc = try_catch.Exception();
    v8::String::Utf8Value err{isolate, exc};
    if (*err) {
        std::cerr << "Exception: " << *err << std::endl;
    }
    
    //Print StackTrace from TryCatch
    v8::Local<v8::Value> trace;
    if (try_catch.StackTrace(context).ToLocal(&trace)) {
        v8::String::Utf8Value trace_str{isolate, trace};
        std::cerr << "Trace: " << *trace_str << std::endl;
    }
    
    //Print stack trace from isolate
    auto stack = v8::StackTrace::CurrentStackTrace(isolate, 100);
    std::cerr << "Stack trace:\n";
    for (int i = 0; i < stack->GetFrameCount(); ++i) {
        auto frame = stack->GetFrame(isolate, i);
        v8::String::Utf8Value function_name{isolate, frame->GetFunctionName()};
        std::cerr
            << "\tat "
            << function_name
            << " (line " << frame->GetLineNumber()
            << ", column " << frame->GetColumn() << ")\n";
    }
}

The exception retrieved with v8::TryCatch::Exception when converted to a string results in error messages like this:

SyntaxError: missing ) after argument list

No line number or column in the message. I get the exact same message when using the v8::TryCatch::StackTrace converted to a string, still no line number.

The stack trace returned by v8::StackTrace::CurrentStackTrace doesn't contain any frames, so I can't get the line number or column there either.

I also tried adding a v8::ScriptOrigin to the v8::Script::Compilefunction, but that did not affect the error message.


Solution

  • It seems the line number is stored in a v8::Message associated with the exception.
    I found the following in an example in the v8 repo:

    void ReportException(v8::Isolate* isolate, v8::TryCatch* try_catch) {
      v8::HandleScope handle_scope(isolate);
      v8::String::Utf8Value exception(isolate, try_catch->Exception());
      const char* exception_string = ToCString(exception);
      v8::Local<v8::Message> message = try_catch->Message();
      if (message.IsEmpty()) {
        // V8 didn't provide any extra information about this error; just
        // print the exception.
        fprintf(stderr, "%s\n", exception_string);
      } else {
        // Print (filename):(line number): (message).
        v8::String::Utf8Value filename(isolate,
                                       message->GetScriptOrigin().ResourceName());
        v8::Local<v8::Context> context(isolate->GetCurrentContext());
        const char* filename_string = ToCString(filename);
        int linenum = message->GetLineNumber(context).FromJust();
        fprintf(stderr, "%s:%i: %s\n", filename_string, linenum, exception_string);
        // Print line of source code.
        v8::String::Utf8Value sourceline(
            isolate, message->GetSourceLine(context).ToLocalChecked());
        const char* sourceline_string = ToCString(sourceline);
        fprintf(stderr, "%s\n", sourceline_string);
        // Print wavy underline (GetUnderline is deprecated).
        int start = message->GetStartColumn(context).FromJust();
        for (int i = 0; i < start; i++) {
          fprintf(stderr, " ");
        }
        int end = message->GetEndColumn(context).FromJust();
        for (int i = start; i < end; i++) {
          fprintf(stderr, "^");
        }
        fprintf(stderr, "\n");
        v8::Local<v8::Value> stack_trace_string;
        if (try_catch->StackTrace(context).ToLocal(&stack_trace_string) &&
            stack_trace_string->IsString() &&
            v8::Local<v8::String>::Cast(stack_trace_string)->Length() > 0) {
          v8::String::Utf8Value stack_trace(isolate, stack_trace_string);
          const char* stack_trace_string = ToCString(stack_trace);
          fprintf(stderr, "%s\n", stack_trace_string);
        }
      }
    }