Node-gyp: c++ exceptions don't work as expected

Created on 24 May 2016  路  3Comments  路  Source: nodejs/node-gyp

I want to catch c++ exceptions that comes from third-party c++ lib that I use in my nodejs addon:

// hello.cc
#include <node.h>
#include <v8.h>
#include <string>

using namespace v8;
using namespace std;

void Method(const v8::FunctionCallbackInfo<Value>& args) {
  try {
    std::string s("abc");
    throw std::runtime_error(s); // third-party c++ lib throw this, I want to catch and get message
  }
  catch (const std::runtime_error &e) {
    printf("err is %s\n", e.what()); // e.what() is empty here!
  }
}

void Init(Handle<Object> exports) {
  Isolate* isolate = Isolate::GetCurrent();
  exports->Set(String::NewFromUtf8(isolate, "hello"),
      FunctionTemplate::New(isolate, Method)->GetFunction());
}

NODE_MODULE(hello, Init)

I use Visual Studio 2013 and I enabled c++ exception like this:

// binding.gyp
{
  "targets": [
    {
      "target_name": "hello",
      "sources": [ "hello.cc" ],
      "msvs_settings": {
        "VCCLCompilerTool": {
          "ExceptionHandling": 1
        }
      }
    }
  ]
}

Then I call my addon like this:

// hello.js
var addon = require('./build/Release/hello');
addon.hello();

And here is output:

err is

exception message is empty! It should contain "abc" string. When I remove msvs_settings section from binding.gyp file it works as expected(output abc string) but compilation gives warning C4530. I don't understand why it doesn't work when I enable exception handling. Looks like when I enable exceptions string is destroyed during stack unwind. However when create new c++ console app(c++ exceptions enabled by default) same code works fine(output abc string). Could you please help me to understand what is wrong?

Most helpful comment

@Flarna thank you very much, It works as expected when I set _HAS_EXCEPTIONS=1(or remove this define). I updated binding.gyp like this;

{
  "targets": [
    {
      "target_name": "hello",
      "sources": [ "hello.cc" ],
      "msvs_settings": {
        "VCCLCompilerTool": {
          "ExceptionHandling": 1
        }
      },
      "conditions": [
        ["OS=='win'", {
          "defines": [
            "_HAS_EXCEPTIONS=1"
          ]
        }]
      ]
    }
  ]
}

All 3 comments

Node and V8 are compiled without exceptions and RTTI so I assume it has something to do with that.

Beyond that, I can't say, I don't use Windows myself. On UNIX platforms, it's critical that you never let the exception cross over into -fno-exceptions code but you don't seem to be doing that.

I think the reason is that the string s is destructed before e.what() is called in case "ExceptionHandling": 1 is set. The std::runtime_error class holds just a pointer to s.c_str() so in the end it's most likely luck that it didn't crash.
Not specifying ExceptionHandling in binding.gyp falls back to /EH which means exceptions are handled but no objects are destroyed (usually not what you want).
"ExceptionHandling": 1 sets /EHsc which is usually what you want; see https://msdn.microsoft.com/en-us/library/1deeycx5.aspx.

If you move the string s outside of the try block it will work as expected.

BTW, I tested this with MSVC 2015.

Seems the std::runtime_error implementation in GCC is significant different - it holds a std::string so it holds a copy and therefore your code will be fine there.

I was wondering about the behaviour I described above so I tried to create a standalone reproducer. Using a "default" VS project above code works, the string is copied.
But for addon builds _HAS_EXCEPTIONS==0 is set which changes the behaviour of MSVC STL to above described behaviour.

@Flarna thank you very much, It works as expected when I set _HAS_EXCEPTIONS=1(or remove this define). I updated binding.gyp like this;

{
  "targets": [
    {
      "target_name": "hello",
      "sources": [ "hello.cc" ],
      "msvs_settings": {
        "VCCLCompilerTool": {
          "ExceptionHandling": 1
        }
      },
      "conditions": [
        ["OS=='win'", {
          "defines": [
            "_HAS_EXCEPTIONS=1"
          ]
        }]
      ]
    }
  ]
}
Was this page helpful?
0 / 5 - 0 ratings