Skip to content

Instantly share code, notes, and snippets.

View gabrielschulhof's full-sized avatar

Gabriel Schulhof gabrielschulhof

  • California, USA
View GitHub Profile

Writing Node.js Bindings - General Principles

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

#define C_HANDLE(jsHandle, cType, destination) \
do { \
cType local = JS#cType::Resolve(jsHandle); \
if (!local) { \
return false; \
} \
*destination = local; \
return true; \
} while (0)
{
"acl": {
"aclist": {
"aces": [
{
"subjectuuid": "*",
"permission": 31
}
]
}
#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))
@gabrielschulhof
gabrielschulhof / Bindings.md
Last active May 11, 2016 16:25
Bindings principles

Writing node.js bindings - general principles

Assumption

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.

  1. 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.
  2. 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
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" );
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)))"; \
#
# 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
#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;
}
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 {