Aborting embedded code in case of exception

59 views
Skip to first unread message

Ryan Ingram

unread,
May 12, 2016, 5:16:09 PM5/12/16
to v8-users
My codebase is currently running on V8 3.30 in case it matters.

I'm writing a marshalling system to link JS function calls to native C++ code.  As part of this system, I need to convert function arguments into native C++ types.  For the sake of example, lets say I'm implementing a simple binding to C's "atan2" function.

    double atan2(double y, double x);

So, an example v8::FunctionCallback for this case

    void V8CallAtan2( const v8::FunctionCallbackInfo<v8::Value>& info )
    {
        v8::Isolate* isolate = info.GetIsolate();

        // Not sure if this is required, since we must be in an isolate scope already to be getting called from script
        v8::Isolate::Scope isolateScope(isolate);

        // Don't hold onto Local handles allocated by this function
        v8::Handle::Scope handleScope(isolate);

        // Some simple checks that we're not being stupid
        if(info.IsConstructCall())
        {
            isolate->ThrowException( v8::String::NewFromUtf8( isolate, "Called 'atan2' as a constructor" ) );
            return;
        }

        if(info.Length() != 2)
        {
            isolate->ThrowException( v8::String::NewFromUtf8( isolate, "Called 'atan2' with incorrect number of arguments." ) );
            return;
        }

        // Extract arguments from the call
        v8::Local<v8::Value> yVal = info[0];
        v8::Local<v8::Value> xVal = info[1];

        // Marshal into native C++ types
        double y = yVal->NumberValue();        // HERE1
        double x = xVal->NumberValue();        // HERE2

        // Call native function
        double result = atan2(y,x);

        // Marshal return value back into a JS type
        v8::Local<v8::Value> resultVal = v8::Number::New(isolate, result);

        // and return it to the caller
        info.GetReturnValue().Set( resultVal );
    }

From reading the source code to NumberValue() it looks like this call could potentially throw a JS exception, which makes sense--the arguments could be of some type that isn't convertible to double.  So, if the line at HERE1 throws a JS exception, if my understanding of ThrowException is correct, I'm not allowed to do any further JS operations (including calling NumberValue() at HERE2), and instead need to immediately return control to v8 by returning from my callback -- as I do in the sanity checks before reading the arguments.

But I haven't been able to find a way to ask the Isoalte if there is a pending exception, at least not in the public API.  How do I detect this case?

I noticed that newer versions of v8 have Value::NumberValue() return Maybe<double> instead, and I'm considering pushing a v8 upgrade for that reason.  But first I want to know if there's a fix without impacting the other users on my team.

Thanks!

  -- ryan

Ben Noordhuis

unread,
May 13, 2016, 2:53:44 AM5/13/16
to v8-users
Here is how you do it:

v8::TryCatch try_catch(isolate); // |isolate| may not be necessary with 3.30.
DoOperationThatCanThrow();
if (try_catch.HasCaught()) {
// Do something with the exception, e.g. try_catch.ReThrow().
}

By the way, in your example, the v8::Isolate::Scope and
v8::HandleScope are not necessary; function callbacks and property
callbacks have them implicitly.
Reply all
Reply to author
Forward
0 new messages