This material is about creating Node.js interfaces for native libraries written in C. It is not so much about the mechanics of writing the code, or about the structure of a npm package containing a native addon, but about the various situations you are likely to face when writing addons. It starts with the assumption that it is best to create an addon that provides as little abstraction as possible so as to allow you to provide a Javascript API to the consumers of your project that is itself written in Javascript. The portion of the Node.js documentation that describes native addons, the V8 reference, and the reference provided by the Native Abstractions for Node ([NAN][]) project give you an ample toolset for creating native addons. Once you've managed to create your first native addon, and are ready to bring a complete native library into the Node.js world, you will be faced with having to translate the artifacts of the C language into Javascript concepts. A
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define C_HANDLE(jsHandle, cType, destination) \ | |
do { \ | |
cType local = JS#cType::Resolve(jsHandle); \ | |
if (!local) { \ | |
return false; \ | |
} \ | |
*destination = local; \ | |
return true; \ | |
} while (0) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"acl": { | |
"aclist": { | |
"aces": [ | |
{ | |
"subjectuuid": "*", | |
"permission": 31 | |
} | |
] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <string.h> | |
#include <sol-oic-client.h> | |
#include <soletta.h> | |
static struct sol_oic_pending *pending = NULL; | |
struct sol_oic_client *client = NULL; | |
struct sol_oic_resource *theResource = NULL; | |
#define MIN(x, y) (((x) < (y)) ? (x) : (y)) |
Writing node.js bindings - general principles
It is best to write as little logic as possible into the bindings itself. Ideally the addon is a one-to-one mapping of the underlying native API itself. Convenient abstractions can then be implemented in Javascript.
Of course the underlying language has artifacts that need not be mapped.
- For example, C has
void *user_data
with its function pointers, but that's only to provide room for context. Javascript is the king of context, so you don't need to map this mechanism into Javascript. OTOH, you need this mechanism for the internals of the bindings. - Another example is pointers that are filled out by the native side. In that case you can have the binding accept an empty object the properties of which it fills out (a receptacle) or you can return a new object you create inside the binding. But what if the native function additionally returns a result code (like
errno
for example)? Do you retain one
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function runAsParent() { | |
var childProcess = require( "child_process" ); | |
function spawnChild( mode, callback ) { | |
console.error( "master: *** Spawning child " + mode ); | |
var theChild = childProcess.spawn( "node", [ __filename, mode ], { | |
stdio: [ process.stdin, "pipe", process.stderr ] | |
} ); | |
theChild.on( "close", function() { | |
console.error( "master: child process " + mode + " has quit" ); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/tools/build/Makefile.targets b/tools/build/Makefile.targets | |
index 073294f..23866be 100644 | |
--- a/tools/build/Makefile.targets | |
+++ b/tools/build/Makefile.targets | |
@@ -212,6 +212,7 @@ bindings-nodejs: $(SOL_LIB_OUTPUT) | |
$(NODEJS) bindings/nodejs/configure-bindings.js | |
$(Q) ( \ | |
+ export CXXFLAGS=""; \ | |
export SOLETTA_CFLAGS="$(addprefix -I,$(abspath $(HEADERDIRS)))"; \ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# Automatically generated file; DO NOT EDIT. | |
# Soletta configuration | |
# | |
HAVE_ACCEPT4=y | |
HAVE_BUILTIN_ADD_OVERFLOW=y | |
HAVE_BUILTIN_MUL_OVERFLOW=y | |
HAVE_BZIP2=y | |
# HAVE_CHECK is not set | |
# HAVE_CHRPATH is not set |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "jshandle.h" | |
// This results in undefined symbols, unless the implementation is moved to the | |
// header | |
template <class T> const char *JSHandle<T>::thePrefix() { | |
static const char *returnValue = "prefix"; | |
return returnValue; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var fs = require( "fs" ); | |
var path = require( "path" ); | |
var async = require( "async" ); | |
var dirToCheck; | |
// Find the closest instance of soletta via its package.json | |
function closestSoletta( startDir ) { | |
for ( dirToCheck = startDir; dirToCheck !== "/"; | |
dirToCheck = path.resolve( dirToCheck, ".." ) ) { | |
try { |