If you need to write a binary addon, here are some basic guidelines to keep in mind:
Just to reinforce what I said before, by writing this module in C/C++ you’re explicitly opting out of any runtime benefit the JIT may provide you. Remember the JIT will watch execution of your functions and optimize and inline those functions on the fly.
To help enforce the previous rule, it’s best to wrap a method defined in C/C++
NODE_SET_METHOD(target, "foo", Foo) you shouldn’t be doing
exports.foo = binding.foo. This puts the burden of argument checking and
validation in the native layer. Instead consider:
Since we’re passing
bar coerced as a string and validating
baz’s input, we
can make more assumptions about the safety of certain actions in the native
You will get the best bang for your buck if you interact only with “primitives”, like:
Just in case there’s any confusion, if the method defined in the native layer
will be called often make sure it does not call
->Get and especially not
->Set on an object passed to it. Doing so will only make you sad. Actually,
it’s ok to call these methods on relatively small
v8::Array’s because you
are just doing normal pointer arithmetic to get the values. It’s the
v8::String based lookups and sets you need to be wary of.
First and foremost, do NOT use C++ exceptions, ever, at all, in your code.
Node is compiled with
-fno-exceptions which doesn’t prevent you from using
exceptions, but does change how things are cleaned up after C++ exceptions are
hit. Do NOT use them. Just don’t.
Try to avoid
throwing exceptions from the native layer. While it’s certainly
possible to do so (and Node does so in places) it won’t be as helpful as you
might want it to be, mostly you won’t really know the what and where of when
something threw on the native side, just that it did. (You can always use very
explicit messages and things like
__LINE__ to help
in that regard … eww)
the native side you want to
assert and die a horrible death, instead of
soldiering on only to have arbitrary memory corruption later.
The idea here being that you have some sort of resource handle that needs to
be reused in subsequent calls to your binding. Again, this is perfectly doable
in C/C++ by following the
node::ObjectWrap pattern, or implementing something
similar yourself, but that’s sacrificing most of what the JIT can provide you.
If you need to handle finalization of the handle after it goes out of scope in
v8::Persistent and then call
to define a callback that will be triggered when the GC is about to collect
Please oh please, use bindings. This simple library takes care of the naming and pathing changes that may occur from various configurations and versions of Node. Most importantly if I’ve compiled a debug version of Node, I’ll actually build and run a debug version of your module and may be able to help figure out what might be going wrong.
Know your tools
Do not be afraid to compile Node with
./configure --debug and get your hands
gdb to figure out just what is causing your module to
crash. There are lots of resources out there to help you with that, but often
just getting the stack trace from a core file will tell you quite a lot about
what you’ve done wrong.
- This code can’t be optimized anymore than the compiler being used
- Do NOT mutate objects in C/C++
- Avoid exceptions in C/C++ whenever possible
- Use the bindings module
- Don’t be afraid to debug