Search code examples
duktape

Catching and recovering from error in C++ function called from Duktape


I have created a plugin for the OpenCPN marine navigation program that incorporates Duktape to provide a scripting capability. OpenCPN uses wxWidgets.

Basically, the plugin presents the user with a console comprising a script window, an output window and various buttons. The user enters their script (or loads it from a .js file) and clicks on Run. The script is run using duk_peval. On return I display the result, destroy the context and wait for the user to run again, perhaps after modifying the script. All this works well. However, consider the following test script:

add(2, 3);

function add(a, b){
    if (a == b) throw("args match");
    return(a + b);
    }

If the two arguments in the call to add are equal. The script throws an error and the user can try again. This all works.

Now I can implement add as a c++ function thus:

static duk_ret_t add(duk_context *ctx){
    int a, b;
    a = duk_get_int(ctx, 0);
    b = duk_get_int(ctx, 1);
    if (a == b){
            duk_error(ctx, DUK_ERR_TYPE_ERROR, "args match");
                 }
    duk_pop_2(ctx);
    duk_push_int(ctx, a+b);
    return (1);
    }

As written, this passes the error to the fatal error handler. I know I must not try and use Duktape further but I can display the error OK. However, I have no way back to the plugin. The prescribed action is to exit or abort but these both terminate the hosting application, which is absolutely unacceptable. Ideally, I need to be able to return from the duk_peval call with the error.

I have tried running the add function using duk_pcall from an outer C++ function. This catches the error and I can display it from that outer function. But when I return from that outer function, the script carries on when it should not and the eventual return from the duk_peval call has no knowledge of the error.

I know I could use try/catch in the script but with maybe dozens of calls to the OpenCPN APIs this is unrealistic. Percolating an error return code all the way back, maybe through several C++ functions and then to the top-level script would also be very cumbersome as the scripts and functions can be quite complex.

Can anyone please suggest a way of passing control back to my invoking plugin - preferably by returning from the duk_peval?


Solution

  • I have cracked this at last.

    Firstly, I use the following in error situations:

    if (a == b){
        duk_push_error_object(ctx, DUK_ERR_ERROR, "args match");
        duk_thow(ctx);
        }
    

    If an error has been thrown, the returned values from duk_peval and duk_pcall are non-zero and the error object is on the stack - as documented

    It is all working nicely for me now.